Point your GitLab client (e.g. python-gitlab) at https://gl.sandboxapis.dev. Paths mirror gitlab.com/api/v4.
Coverage badge
Deep554/755 · 73%
The share of read rows with a final answer — served and verified, a deviation, retired upstream, or a reviewed empty/refusal. Computed in the coverage manifest, copied here.
gl.sandboxapis.devServed & verified
329 / 44%
Answering with real universe data, each response checked against GitLab's published spec by the conformance suite on this build. 73 of them are marked a deviation — served faithfully, but failing the vendored spec.
The full read API
755
Every read surface GitLab publishes, deferred long tail included, minus the rows excluded by policy. Writes are out of scope: this universe is read-only.
Not served yet
426
Each returns an explicit, provider-shaped coverage error naming the gap — never invented data.
01 / The swap
No SDK of ours, no shim, no recorded fixtures. The same client library you already use, one environment variable different.
curl https://gl.sandboxapis.dev/api/v4/projects/olympus-labs%2FparthenonVerified drop-in clients
renovate44.39.2The versions the conformance suite drives against this host on every build — pinned in coverage/client-pins.yaml, and watched weekly for upstream releases, because a client library moving without us is how a shipped integration breaks silently.
02 / Coverage by family
All 755 rows the coverage manifest carries for GitLab, deferred long tail included and nothing capped. Open a family, or filter by path to find the exact endpoint your client calls.
Status — what a conformance test found
Mode — what kind of answer a row gets
A row with no mode shown has not been judged yet. Modes are the manifest's own words, from coverage/MODES.yaml; every empty and refuse carries a written reason and a review date before it counts as final.
Badge — where Deep starts
REST + GraphQL + git rows that are not `excluded`. Write operations are NOT rows (DECISIONS 2026-09-01 decision 9): they are counted in meta.write_operations and never enter this ratio.
755 read surfaces in 107 families
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
no broadcast message has ever been posted on this instance: a broadcast exists only because an admin created one, and this universe has no admin acting on it
SERVED 2026-09-12 (coverage wave W8): `/api/v4/version` plus the two facts GitLab's Metadata object adds, and all of them are facts about THIS REPLICA rather than about olympus-labs — so the row needs no artifact read and no generation gate. `version` and `revision` come from GITLAB_METADATA in packages/renderer-gitlab/src/ids.ts, the same constant `/api/v4/version` reads, and conformance asserts the two endpoints agree. `kas.enabled` is FALSE because this host runs no Kubernetes agent server — `/projects/{id}/cluster_agents` is classified `generate` for exactly that reason — and the three fields GitLab renders off that server's own configuration are omitted rather than nulled. `enterprise` is TRUE and it is checkable rather than decorative: this host serves `/groups/{id}/epics`, `/groups/{id}/iterations` and `/projects/{id}/approval_rules`, every one of them EE-only, and conformance drives one of them to prove it
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — the collection this id would index is empty in this universe, so the only honest answer is the vendor's own 404
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
this organization's one deploy token is scoped to its one REPOSITORY, which is where a registry-pull token belongs: reader.listCredentials("deploy-token") (reader.ts:3085) holds one row and its scope_ref is a RepoId, so the GROUP-level list is truthfully empty while /api/v4/projects/{id}/deploy_tokens serves that row. SERVED 2026-09-10 as GitLab's empty page with the pagination headers the vendored operation's page/per_page declare, behind the hello-16 credential gate — an older artifact answers the named generation gap, not []. The subgroup resolves here and is empty for a second reason: it owns no repository and so has no registry to pull (founder ruling F1). RULED by the founder 2026-09-10
a group SSH CERTIFICATE is a certificate AUTHORITY an administrator registers so members can authenticate with short-lived certificates. Registering one is an ACTION and canon records none in any generation; every `ssh-auth` row in reader.listCredentials is a plain ed25519 public key rather than a certificate, which is the same fact read from the members' end. SERVED 2026-09-10 as GitLab's empty page with the pagination headers the vendored operation's page/per_page declare, UNGATED — the fact is a property of the model rather than of the credential domain, so an older artifact answers the same empty list. The vendored document scopes this operation to "a specified group" and not to a top-level one, so the subgroup answers it too. RULED by the founder 2026-09-10
SERVED 2026-09-12 (hello-17): the groups and projects the caller's token reaches, derived from the caller's own membership rows and the projects this organization owns; access_level is their DIRECT standing where they hold one and their organization standing otherwise, which is GitLab's inheritance rule. THE VENDORED DOCUMENT POINTS THIS OPERATION'S 200 AT APIEntitiesPersonalAccessToken - the token, not its associations - contradicting its own summary and description; invariant #2 mirrors the provider, so the body is {groups, projects} and the permissive vendored schema validates it anyway. Paged as ONE sequence, groups first, then split back into the two keys
SERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): canon.CheckoutKey IS a deploy key — `CheckoutKeyType` is literally "deploy-key" — and reader.listCheckoutKeys(repo) already carries the OpenSSH line and BOTH fingerprints in the forms GitLab prints them. `title` is the comment at the end of that same line, READ OFF the material rather than kept as a second copy, so the title and the key can never name two different things; `created_at` is created_epoch; `fingerprint` is the colon-hex MD5 and `fingerprint_sha256` the OpenSSH form. `expires_at`, `last_used_at` and `usage_type` are OMITTED rather than nulled: this universe issues no key with an expiry, records no use of one and stores no usage flag, all three are optional, and a null typed `string` would buy a deviation for nothing. GENERATION-GATED: an artifact without the CI-configuration domain answers that gap rather than `[]`, because `[]` would say this project's CI clones with nothing. `can_push` is FALSE and it is a fact rather than a default: a checkout key exists so CI can CLONE, and nothing in this universe pushes with one. The two `projects_with_*_access` arrays are omitted here, as real GitLab's `Entities::DeployKeysProject` omits them — `can_push` carries the same fact on a project-scoped read
SERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): canon.CheckoutKey IS a deploy key — `CheckoutKeyType` is literally "deploy-key" — and reader.listCheckoutKeys(repo) already carries the OpenSSH line and BOTH fingerprints in the forms GitLab prints them. `title` is the comment at the end of that same line, READ OFF the material rather than kept as a second copy, so the title and the key can never name two different things; `created_at` is created_epoch; `fingerprint` is the colon-hex MD5 and `fingerprint_sha256` the OpenSSH form. `expires_at`, `last_used_at` and `usage_type` are OMITTED rather than nulled: this universe issues no key with an expiry, records no use of one and stores no usage flag, all three are optional, and a null typed `string` would buy a deviation for nothing. GENERATION-GATED: an artifact without the CI-configuration domain answers that gap rather than `[]`, because `[]` would say this project's CI clones with nothing. The same row by id, byte-identical to the list's entry, which conformance compares directly; a key id nobody holds is GitLab's own 404
SERVED 2026-09-10 (hello-16): reader.listCredentials("deploy-token") filtered on scope_ref (reader.ts:3085). `username` is GitLab's OWN derivation, gitlab+deploy-token-{id}, so it is the real value and moves with the id. The scope vocabulary is a DIFFERENT closed set from a PAT's — a deploy token cannot push and cannot reach the API — so canon's scopes map through their own table and it throws on a scope no deploy token can hold. `revoked` and `expired` are read off canon's anchored state and never off a clock, so no Date.now() decides whether a token still works. `expires_at` IS a timestamp here, unlike a personal token's. Gated on hasCredentials()
SERVED 2026-09-10 (hello-16): the same row by id, byte-identical to the list's first entry
SERVED 2026-09-12 (hello-17): the caller's OpenPGP keys, from the founding member's four kinds. The armored `key` block stays omitted rather than invented: a transferable key needs a self-signature made with a private half this universe never generates, and an armored block that failed to import would be a placeholder in a convincing costume (the hello-16 GPG ruling, unchanged). Answers the named hello-17 generation gap on every older artifact
SERVED 2026-09-12 (hello-17): the by-id twin of the caller's GPG key list, resolving exactly what that list carries
SERVED 2026-09-12 (hello-17): features.callerCredentials makes the FOUNDING MEMBER hold one of every credential kind this organization accepts, and defines the caller as the founding member - so the persona GET /user reports now holds an ssh-auth key, a signing key, a GPG key and an ACTIVE personal token. This route serves both SSH kinds off credentialsForPerson told apart by usage_type, and the rows are byte-identical to /users/{id}/keys for the same person (one canonical person rendered once). On hello-16 and all 127 registered pins it answers the named hello-17 generation gap and never [], because an empty caller key list reports a fact about WHO THE CALLER IS rather than about this organization
SERVED 2026-09-12 (hello-17): the by-id twin of the caller's SSH key list, resolving exactly what that list carries so the two cannot disagree about what this caller holds. Blocked until now by the same fact - the caller held no key, so no id could ever return 200 - and unblocked by the same one
SERVED 2026-09-10 (hello-16): reader.credentialsForPerson(person, "gpg") (reader.ts:3075). THE ARMORED `key` FIELD IS OMITTED AND THAT IS THE HONEST ANSWER: `APIEntitiesGpgKey` has three properties and `key` is the ARMORED TRANSFERABLE key, which is a public-key packet PLUS a user id PLUS a self-signature binding them — and a self-signature needs a private half this universe never generated (decisions/2026-09-09-1347). An armored block emitted here would fail `gpg --import`: a placeholder that LOOKS importable. The component declares no `required` list, so a response without `key` is a legal one; `null` would additionally violate its declared `type: string`. GitHub's shape has somewhere honest to put the material (a required `public_key` beside a nullable `raw_key`) and GitLab's does not. `id` and `created_at` are the row's own
SERVED 2026-09-10 (hello-16): the same rows by id, byte-identical to the list's first entry (conformance asserts it). The armored-key omission is the list row's ruling and applies here unchanged
SERVED 2026-09-10 (hello-16): the same rows addressed by gitlabId(PersonCredentialRow.id) — conformance asserts the by-id form is byte-identical to the list's own first row, so the two cannot describe one key two ways. An id the person does not hold is GitLab's own 404 Key Not Found
SERVED 2026-09-10 (hello-16): reader.credentialsForPerson(person, "ssh-auth") + ("ssh-signing") (packages/artifact/src/reader.ts:3075). ONE LIST FOR BOTH GRANTS, which is GitLab's shape rather than GitHub's: an authentication key and a signing key are told apart by `usage_type` (auth/signing) instead of by two endpoints, so canon's two kinds survive the crossing. `key` is the artifact's own OpenSSH line and re-hashes to its `fingerprint_sha256` under ssh-keygen -lf, which conformance recomputes; `expires_at` and `last_used_at` are omitted when canon carries none (the vendored component declares no required list and types both non-nullable, the deploy-key precedent). Gated on reader.hasCredentials(): every pre-hello-16 pin answers the noCredentials generation-gap 404 rather than [], because an empty key list asserts that this person pushes with nothing
SERVED 2026-09-10 (hello-16): reader.listCredentials("group-token") filtered on scope_ref (reader.ts:3085) — one row, the roster-sync token scoped to the organization's own group. THE SUBGROUP RESOLVES AND ANSWERS []: it owns no repository and has no automation to run (founder ruling F1, 2026-09-09), and conformance drives both ids. `access_level` is the holder's own organization standing, the same derivation the project rows use. Unpaginated, per the vendored operation. Gated on hasCredentials()
SERVED 2026-09-10 (hello-16): the same row by id, byte-identical to the list's first entry. The organization's token id does NOT resolve under the subgroup — conformance asserts it, because a token belongs to one group rather than to every group on the host
SERVED 2026-09-10 (hello-16): reader.credentialsForPerson(athena, "personal-token") (reader.ts:3075) — "all personal access tokens accessible by the authenticated user", which on a NON-ADMINISTRATOR is their own, and athena is who GET /user reports. A TOKEN IS METADATA AND NEVER A VALUE: name, scopes, creation, expiry, last use and state, because a provider shows a token's value once at creation and there is no creation here (the PagerDuty routing-key rule). Canon's neutral TokenScopes map to GitLab's coarser vocabulary in one place (render-credentials.ts) and the mapper THROWS on a scope it has never heard of rather than dropping it. `expires_at` is GitLab's DATE form — its own docs example is "2021-01-31" while created_at/last_used_at beside it are timestamps — which the vendored document mistypes date-time, so the row is a deviation. `last_used_ips` is omitted: an IP means something outside this universe and canon records none. Gated on hasCredentials()
SERVED 2026-09-10 (hello-16): the caller's own tokens by id, byte-identical to the list's first row. Another member's token id answers GitLab's 404 — the same non-administrator visibility the list reports, read from the other end, and conformance drives it
SERVED 2026-09-12 (hello-17): `self` names the token the REQUEST AUTHENTICATED WITH, and hello-17 gives the caller one that works: the founding member holds a personal token by construction and the setup-token rotation keeps it live, so the row answers an ACTIVE token rather than the expired one that would have described a request GitLab would have answered 401. The PENDING request canon also carries can never surface here - credentialScope() excludes state='pending' from every list accessor, and credentialRequests() is the only place a request is reachable
SERVED 2026-09-10 (hello-16): reader.listCredentials("project-token") filtered on scope_ref (reader.ts:3085) — TWO ROWS, AND THEY ARE THE ROTATION: the CI token in force when the incident opened was revoked at the incident's first instant and a replacement minted in the same breath, so the revoked row's ended_epoch IS the active row's created_epoch (conformance asserts they abut). `user_id` is the principal the token acts as — this universe's CI identity, a person a client can then fetch, where real GitLab mints a bot user. `access_level` is that holder's OWN standing in the repository, read off reader.membershipFor("repo", …) and mapped once by GITLAB_ACCESS_LEVEL: GitLab's own rule is that a resource token cannot exceed its creator's role. Unpaginated, because the vendored operation declares neither page nor per_page. Gated on hasCredentials()
SERVED 2026-09-10 (hello-16): the same two rows by id, byte-identical to the list's first entry. A DEPLOY token's id does NOT resolve here and conformance asserts it — GitLab keeps the two families in separate tables and so does canon
SERVED 2026-09-12 (coverage wave W8): the deployment names a commit and reader.pullsForCommit resolves the merge requests that commit belongs to — the ones listing it among their commits plus the one whose MERGE commit it is, which is the shape every staging deployment in this universe takes. THE SAME READ backs the DORA lead-time fold, so a lead time and this list cannot disagree about which changes went out. The rows are `glMergeRequest`'s own, carrying the iid `/merge_requests/{iid}` answers to — conformance fetches each one by that iid and asserts it is the same merge request — and the same nullable-key `deviation` the merge-request list already declares. A deployment id nobody has is GitLab's own 404
SERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): canon.CheckoutKey IS a deploy key — `CheckoutKeyType` is literally "deploy-key" — and reader.listCheckoutKeys(repo) already carries the OpenSSH line and BOTH fingerprints in the forms GitLab prints them. `title` is the comment at the end of that same line, READ OFF the material rather than kept as a second copy, so the title and the key can never name two different things; `created_at` is created_epoch; `fingerprint` is the colon-hex MD5 and `fingerprint_sha256` the OpenSSH form. `expires_at`, `last_used_at` and `usage_type` are OMITTED rather than nulled: this universe issues no key with an expiry, records no use of one and stores no usage flag, all three are optional, and a null typed `string` would buy a deviation for nothing. GENERATION-GATED: an artifact without the CI-configuration domain answers that gap rather than `[]`, because `[]` would say this project's CI clones with nothing. `Entities::DeployKey` RATHER THAN `DeployKeysProject`, which is why this row alone publishes the two project arrays and carries no `can_push`: the whole point of a user-scoped key list is saying WHICH projects each key reaches. `projects_with_write_access` is EMPTY and the project sits under `projects_with_readonly_access`, because `can_push` is false on these keys — listing the project as a write grant while the project-scoped row says the key cannot push would be this host contradicting itself about one key (invariant #5). Both arrays are a `deviation`, the vendored component typing each as a single object while real GitLab sends a collection. The PERSON still has to resolve, so an id nobody holds is GitLab's own 404 rather than a list about nobody
refuse — listing every key, deploy token or impersonation token across the instance requires admin on GitLab; answering would be inventing an admin session
refuse — listing every key, deploy token or impersonation token across the instance requires admin on GitLab; answering would be inventing an admin session
refuse — the by-id twin of the group deploy-token list: that list is truthfully empty, so no id can resolve and GitLab's own 404 is the whole answer rather than a coverage miss. SERVED 2026-09-10 with x-sandboxapis-refusal naming this row, and UNGATED unlike the list — no generation of this universe holds a group-level deploy token, so the 404 is true on every artifact and naming the credential generation gap here would point a caller at a live host that answers the identical 404. The group still has to resolve: an id nobody holds is 404 Group Not Found, and the subgroup gets this same refusal. RULED by the founder 2026-09-10
refuse — every actor in olympus-labs is a person on the roster — canon mints no bot principal, and a service account served here would be a member no other host lists (invariant #5); GitLab answers `[]` at 200 for a group with none. THE NAMED HALF: `{user_id}` names a service account, and no service account exists to be named — so this operation never reaches a token list to be empty of and GitLab's own 404 is the whole answer. UNGATED: canon has no `ServiceAccount` in any generation — hello-17 dropped the federation domain that would have introduced one. RULED by the founder 2026-09-12
refuse — listing every key, deploy token or impersonation token across the instance requires admin on GitLab; answering would be inventing an admin session
refuse — listing every key, deploy token or impersonation token across the instance requires admin on GitLab; answering would be inventing an admin session
refuse — every actor in olympus-labs is a person on the roster — canon mints no bot principal, and a service account served here would be a member no other host lists (invariant #5); GitLab answers `[]` at 200 for a group with none. THE NAMED HALF: `{user_id}` names a service account, and no service account exists to be named — so this operation never reaches a token list to be empty of and GitLab's own 404 is the whole answer. UNGATED: canon has no `ServiceAccount` in any generation — hello-17 dropped the federation domain that would have introduced one. RULED by the founder 2026-09-12
refuse — listing every key, deploy token or impersonation token across the instance requires admin on GitLab; answering would be inventing an admin session
refuse — listing every key, deploy token or impersonation token across the instance requires admin on GitLab; answering would be inventing an admin session
SERVED 2026-09-12 (hello-17): an HONEST EMPTY, and gated. features.reviewPolicy exists and the route QUERIES the container's own id against scope_kind/scope rather than hard-coding [] - so it serves a namespace-scoped rule the day canon writes one. None exists: a rule is measured off the merges in its scope, the organization's merges all happen in its one project, and the subgroup owns no repository (founder ruling F1), so neither group id has a rule of its own. On every artifact without the domain the route answers the named hello-17 generation gap instead, because [] there would assert that nothing gates a merge in an organization whose every merge carried reviews
SERVED 2026-09-12 (hello-17): the group-level setting is its projects' rules read from above - GitLab's own cascade, where a group setting governs every project the group owns and a subgroup inherits its parent's. This organization owns one project, so the rules in scope are that project's and both group ids answer the same body. One field, for the reason the project twin gives
SERVED 2026-09-12 (hello-17): one field, and it is the one canon answers: allow_overrides_to_approver_list_per_merge_request IS canon's `optional`, which says whether a rule may be overridden on a single merge request. The other six settings this entity carries are policy toggles canon does not model and are omitted rather than defaulted, the same ruling /projects/{id}/approvals takes
SERVED 2026-09-12 (hello-17): the project's rule as one merge request sees it. source_rule reports the project rule's requirement and `overridden` is read off canon's `optional` rather than hard-coded - nothing in this universe writes a per-merge-request rule, and a rule canon marks non-overridable cannot have been overridden
SERVED 2026-09-12 (hello-17): the by-id twin of the merge request's approval-rule list, byte-identical to the same rule inside it
SERVED 2026-09-12 (hello-17): how far along one merge request is: approved_by is MEASURED from the reviewers whose verdict was `approve` - the same review table canon derived the rule's approver list from - and `approved` is the arithmetic of approved_by against approvals_required, so a client can recompute it from the two fields printed beside it. Every approver is checked to be an eligible one, and /merge_requests/{iid}/approvals (green since the depth wave) now reads approvals_required off the same rule so the two surfaces cannot disagree
canon models no dependency between merge requests, so no merge request blocks or is blocked by another
the other direction of the merge-request dependency pair, held to the same fact: canon models no dependency between merge requests
a context commit is one a person manually ATTACHES to a merge request to widen the diff a reviewer sees. It is an action, canon models no such action, and so no merge request carries one. Unpaginated: the vendored document declares no page/per_page on this operation. RULED by the founder 2026-09-09
a draft note is an unsubmitted review comment, by definition never visible to anyone but its author; every review in this universe is already submitted, so the draft list is truthfully empty
"all merge requests for a specified group AND ANY SUBGROUPS", which for the organization is every merge request in the universe. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers `200 []`: it owns no project and has no subgroup below it (founder ruling F1)
SERVED 2026-09-12 (hello-17): features.reviewPolicy gives the repository the rule its merges were actually held to - approvals_required MEASURED off the merged pulls, approvers derived from the people the review log records approving. rule_type is `regular` by GitLab's own definition (a rule that names its approvers), eligible_approvers IS users because `groups` is empty, and `groups` is empty as a fact about GITLAB'S MODEL rather than about the policy: canon's approver TEAMS have no GitLab container on this host, which publishes exactly two groups and neither is a team. applies_to_all_protected_branches is true because canon's rule carries no branch filter; the vendored component types the accompanying protected_branches as a single object where real GitLab sends an array, so the row is a deviation
SERVED 2026-09-12 (hello-17): the by-id twin of the project's approval-rule list, byte-identical to the same rule inside it
SERVED 2026-09-12 (hello-17): the private settings surface over the same rule, rendered by the same shaper as /approval_rules so the two cannot drift. fallback_approvals_required is the rule's own number because canon carries ONE approval measurement for a repository - what its merges actually carried - and there is no second figure for a fallback to differ by. `target_branch` narrows nothing and is answered rather than refused, because the rule applies to every protected branch. The vendored component types `rules` as a single object where real GitLab sends an array, so the row is a deviation
SERVED 2026-09-12 (hello-17): the project's approval configuration: approvals_before_merge is the rule's own requirement and disable_overriding_approvers_per_merge_request is canon's `optional` read from the other end. `approvers` and `approver_groups` are the empty arrays real GitLab sends on any instance that uses approval RULES, which this one does - not a gap - and the vendored components type both as single objects, so the row is a deviation. The seven policy toggles beside them (reset-on-push, author approval, committer approval, password to approve) are OMITTED rather than defaulted: canon models no such policy and a chosen false would be a placeholder wearing a boolean's clothes
no persona in olympus-labs logs time against an issue or a merge request — canon records no TimeEntry; GitLab's own answer for an untracked issue is a 200 carrying `time_estimate: 0`, `total_time_spent: 0` and both human_ fields null. NOT A COLLECTION: the operation returns `APIEntitiesIssuableTimeStats`, an object, so the honest empty is the zeroed object rather than `[]`. It is the SAME object `glIssue` and `glMergeRequest` already embed as `time_stats` (`emptyTimeStats` in packages/renderer-gitlab/src/render2.ts), read from one function so the standalone endpoint and the embedded block can never disagree (invariant #5). canon's `Issue.estimate` is a story-point estimate on a fibonacci scale rather than a duration, and `time_estimate` is documented in seconds, so mapping one onto the other would be an invention. The two human_ fields are a `deviation` against the vendored component, which types them `string` with no `nullable` while real GitLab renders nil for a zero — the same two entries NULLABLE_SPEC_BUGS already carries for the embedded block. UNGATED: no generation of this universe records a time entry. RULED by the founder 2026-09-12
refuse — the collection this id would index is empty in this universe, so the only honest answer is the vendor's own 404
refuse — the collection this id would index is empty in this universe, so the only honest answer is the vendor's own 404
refuse — the head of refs/merge-requests/{iid}/merge — a commit produced by MERGING source into target. GitLab writes that ref from a mergeability check and refuses with the documented 400 when the check does not succeed, which is every settled merge request; for an open one canon records no merge result and the sha would have to be invented. RULED by the founder 2026-09-09
every member counts against the seats: canon models no guest, no minimal access and no service account, so nobody here holds a membership without occupying one. THE SUBGROUP RESOLVES HERE as of 2026-09-10 AND ANSWERS GITLAB'S OWN 404: the vendored document says this lists billable members "of a specified TOP-LEVEL group", so the operation does not exist for a subgroup — a different fact from having no billable member, and a `200 []` would report the second while the provider reports the first
there IS a second group as of hello-16 and this is still empty, because a SHARE is an action and canon records none: no group here has ever been shared with another. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers the same empty list, for the same reason
an INVITATION between groups is an action and canon records none, so no group here has been invited to another. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers the same empty list, for the same reason
the counts over the same rows the group issue list serves. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers REAL ZEROS — `{all: 0, closed: 0, opened: 0}` — rather than an empty object: a group whose issue list is empty has zero of each, which is what real GitLab returns
reader.orgMembers() for the organization's own group. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers `200 []`: the vendored document is explicit that this lists "all DIRECT members ... does not return inherited members from ancestor groups", and no `membership` row in this artifact scopes to a namespace (decisions/2026-09-09-1742). /members/all, which DOES include ancestors, answers the full roster for it — the two are GitLab's own split rather than one of them being a stub. Before 2026-09-10 this row answered `404 Group Not Found` for an id the same host lists, which was invariant #5 failing from the inside
one DIRECT member of the group. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers `404 Not found` for every person: it has no direct member, which is the same fact its list row reports as `[]`. The inherited form /members/all/{user_id} is where an organization member resolves
the direct-plus-INHERITED listing over one membership table. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers the ORGANIZATION'S FULL ROSTER for it, which is the vendored document's own semantics — "also returns inherited members from ancestor groups" — and the one family in this sweep where the parent's data flows DOWN rather than the child being empty
one member of the direct-plus-inherited listing. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and resolves every organization member through inheritance, matching its own list row; the DIRECT-only twin /members/{user_id} answers GitLab's 404 there instead
SERVED 2026-09-12 (coverage wave W3): the same rows /api/v4/groups/{id}/invitations serves, in GitLab's pending-member shape. THIS CORRECTS A FALSIFIED EMPTY: the row was terminal on the reason that this group has no outstanding invitation, and the artifact carries two org-scope memberships that are pending with an invitee_email - one of which GitHub has served at /orgs/{org}/invitations since the hello-16 access wave. The vendored description is the wider operation - members in an awaiting state AND those invited without a GitLab account, across the group and any subgroups and projects - and this universe makes the two coincide: canon has no awaiting state (a Membership is active or pending, and a pending one with an address is an invitation) and no subgroup- or project-scoped pending row exists, which conformance asserts rather than assumes. `invited` is person IS NULL AND invitee_email IS NOT NULL; `approved` is true because nobody here is awaiting an administrator's approval, a state canon models nowhere; `name`, `username` and `web_url` are omitted because there is no account to name. The vendored operation declares no schema, so those assertions are the row's whole check. THE SUBGROUP STILL ANSWERS GitLab's 404 - the document says the operation works on top-level groups only. Every artifact without the membership domain goes on answering the same empty list it always did, because reader.listMemberships returns nothing there
a pending reassignment is an IMPORT artifact and this group was never imported from another instance. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers the same empty list, for the same reason
the projects a group owns. The organization owns the one repository; the hello-16 subgroup owns NONE by founder ruling F1 (repo.namespace is null on every row), so this list is truthfully empty for it and the row resolves both group ids rather than 404ing one of them.
the organization owns every project directly and none is shared in from anywhere. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers the same empty list: it owns no project of its own (founder ruling F1) and nothing has been shared with it either
SERVED 2026-09-12 (hello-18): THE SAME SIXTEEN PEOPLE ANSWER saml_users, provisioned_users AND enterprise_users, and that is GitLab's own definition rather than a shortcut: an enterprise user is one the group provisioned through its own SAML/SCIM, and a SAML user and a provisioned user are that sentence read from the other two ends. canon carries ONE identity_provider_config (protocol saml, scim_enabled 1) and one external_uid per person, so the three lists are one set and conformance asserts they are identical rather than leaving it a coincidence. THE MACHINE ACCOUNT IS EXCLUDED and that is the domain's load-bearing ruling (decisions/2026-09-12-1120): canon writes no external_uid for the CI bot, because a directory provisions humans and GitLab's word for a machine principal is a SERVICE ACCOUNT — a different object, a different API family, and one five armed `…/service_accounts` rulings say this universe does not mint. The rows are glUser, byte-identical to what /api/v4/users/{id} serves for each of them, which conformance checks person by person. THE SUBGROUP RESOLVES THIS ONE AND ANSWERS []: it is the one of the three the vendored document does NOT restrict to a top-level group, and a subgroup runs no directory of its own, so it has provisioned nobody. Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)
SERVED 2026-09-12 (hello-18): THE SAME SIXTEEN PEOPLE ANSWER saml_users, provisioned_users AND enterprise_users, and that is GitLab's own definition rather than a shortcut: an enterprise user is one the group provisioned through its own SAML/SCIM, and a SAML user and a provisioned user are that sentence read from the other two ends. canon carries ONE identity_provider_config (protocol saml, scim_enabled 1) and one external_uid per person, so the three lists are one set and conformance asserts they are identical rather than leaving it a coincidence. THE MACHINE ACCOUNT IS EXCLUDED and that is the domain's load-bearing ruling (decisions/2026-09-12-1120): canon writes no external_uid for the CI bot, because a directory provisions humans and GitLab's word for a machine principal is a SERVICE ACCOUNT — a different object, a different API family, and one five armed `…/service_accounts` rulings say this universe does not mint. The rows are glUser, byte-identical to what /api/v4/users/{id} serves for each of them, which conformance checks person by person. THE SUBGROUP GETS GITLAB'S OWN not_found!: the vendored description scopes this operation to a specified TOP-LEVEL group, so the operation does not exist for a subgroup — a different fact from having nothing to list, and the one the provider reports (the per-family table of decisions/2026-09-10). Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)
the namespaces a GROUP can be transferred into. Empty for the organization because a group cannot be transferred into its own descendant and the only other namespace is one. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and is empty for the OPPOSITE reason, which is why this row now carries both: the only candidate for the subgroup is the parent it already sits under, and a transfer that moves nothing is no transfer. Neither emptiness is a gap
SERVED 2026-09-12 (hello-18): the group's own uploads — reader.namespaceUploads. THE ROOT GROUP HOLDS NONE AND THE SUBGROUP HOLDS ONE, which is the split /groups/{id}/labels already makes and is a fact rather than a gap: a group-level upload belongs to the CONTAINER it was attached to, and canon attaches its one such file — the chart of the release the subgroup was created around — to the subgroup. So the root answers [] at 200 (the group resolves, it simply has nothing attached) and the subgroup answers its file; conformance drives BOTH ids and asserts each. Gated on hasAttachmentParents(); an older artifact answers the named hello-18 upload gap rather than [], because [] there would say nobody here ever attached a file — false of the story every generation tells, where only the PARENT column was missing
SERVED 2026-09-12 (hello-18): the FILE. The vendored operation declares no response schema at all, because the body is the bytes: this route answers the artifact's own attachment.body verbatim, with the artifact's own content_type and `Content-Disposition: attachment` naming the file. The disposition is `attachment` for every kind rather than a branch this host guesses at — GitLab serves an upload inline only for a type on its own safe list, and image/svg+xml, which every image in this canon carries, is explicitly not on it because an SVG can carry script. Conformance re-reads the artifact row and asserts the served bytes are identical to it and that their length is the `size` the list advertised. THE `secret` IS THE FILE'S OWN DIGEST, first 32 characters. GitLab mints a 32-character hex token per upload and canon mints none; an invented one would be a value no client could cross-check, while the first half of attachment.sha256 — measured from the body at compile — is 32 hex characters, is a pure function of the bytes it addresses, and is reproducible by any caller that has downloaded the file. Conformance asserts the secret address and the id address serve the same bytes, and that a secret which is not this file's digest is GitLab's own 404. Gated on hasAttachmentParents(); an older artifact answers the named hello-18 upload gap
SERVED 2026-09-12 (hello-18): the FILE. The vendored operation declares no response schema at all, because the body is the bytes: this route answers the artifact's own attachment.body verbatim, with the artifact's own content_type and `Content-Disposition: attachment` naming the file. The disposition is `attachment` for every kind rather than a branch this host guesses at — GitLab serves an upload inline only for a type on its own safe list, and image/svg+xml, which every image in this canon carries, is explicitly not on it because an SVG can carry script. Conformance re-reads the artifact row and asserts the served bytes are identical to it and that their length is the `size` the list advertised. Scoped to the group named in the path, so a project upload's id is GitLab's 404 here exactly as a group upload's is on the project path. Gated on hasAttachmentParents(); an older artifact answers the named hello-18 upload gap
no pending repository invitations in this universe — the repository roster is the approvers whose organization role is a plain member, plus the CI identity, and all of them were GRANTED rather than offered; GitLab's own answer for a project with none is `[]` at 200. THE GROUP TWIN IS NOT EMPTY and is served with real rows: hello-16 models an invitation as a pending membership carrying an invitee_email, two org-scope memberships are in exactly that state, and /groups/{id}/invitations has served them since wave W3 — what canon does not carry is a REPOSITORY-scoped pending membership, since reader.listMemberships("repo", repo) holds six rows and every one is active. The same reading the founder made of GitHub's twin /repos/{owner}/{repo}/invitations on 2026-09-10. UNGATED: every repository membership in every generation is granted rather than offered, including the four that predate features.memberships and carry no membership table at all. Falsifiable: the `expectsEmpty` conformance entry reds this row the day the generator offers a repository seat. RULED by the founder 2026-09-12 (round 20)
SERVED 2026-09-12 (hello-17): WAS `empty` AND THE RULING WAS FALSIFIED ON SCHEDULE. The 2026-09-09 namespaces decision said in as many words that the day canon gives a namespace a member this goes red rather than hiding it; features.grants gives the subgroup a roster of five - the people whose authority is over the PLAN rather than over the code - so this route serves that person's namespace membership, with created_at read off the membership row's own instant rather than the person's first sighting. A person on no namespace roster still gets [], which is what keeps this a derive rather than a claim that everybody is in the subgroup. THE SUBGROUP STILL ANSWERS GITLAB'S OWN 404: the vendored document says the operation works on top-level groups only. On every artifact without the domain namespaceMembers() finds nothing and the answer is the [] all 127 registered pins serve
the memberships that put one person on a seat: the group, then every project it owns — the same two levels /users/{id}/memberships reports. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers GitLab's 404, with the rest of the billable-members family: it addresses a billable member OF a group whose own billable list does not exist for a subgroup, and a member of a list that 404s cannot resolve (invariant #5 from the other end)
the transitive twin of the subgroup list, from the same reader.listNamespaces walk. One level deep in this universe, so it answers the same single row; older pins still answer the empty list. SERVED 2026-09-09 (hello-16)
SERVED 2026-09-12 (coverage wave W3): the OUTSTANDING invitations on this group - reader.listMemberships('org', org) restricted to state pending with no person, an invitee_email and no failed_epoch. GitHub serves exactly these rows at /orgs/{org}/invitations, so the two hosts name the same outstanding offers. A LAPSED offer is excluded and the exclusion is asserted: canon marks an expired invitation with failed_epoch and GitHub publishes those separately, while GitLab has no failed-invitation surface at all - listing one as pending would tell a client an offer can still be accepted when it cannot. `invite_token` is omitted and always will be: it is the secret a recipient redeems the invitation with, and canon's no-secret contract means no row carries one. `user_name` is omitted because every invitation here was sent to an ADDRESS with no account behind it. `access_level` is the integer real GitLab sends where the component types it string, so the row is a deviation. The subgroup resolves and answers its own set. The old reason - a new canon entity Invitation is needed - was falsified by hello-16's membership domain, which models an invitation as a pending membership with an address
every issue in the organization's projects. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers `200 []`: it owns no repository (founder ruling F1, 2026-09-09) and has no subgroup of its own, so the rollup is empty BY CONSTRUCTION rather than by a gap — the artifact asserts `repo.namespace IS NULL` on every row and conformance re-checks it
features.namespaces (hello-16) gives this organization one subgroup, so reader.listNamespaces(parent) answers a real child. The 2026-09-09 empty said a subgroup was on hello-16 and this is that generation; older pins still answer the empty list, which is what they truthfully hold. SERVED 2026-09-09 (hello-16)
generate — A WIDENING, NOT A BUILD: canon.AdminAuditEvent exists (packages/canon/src/support-desk.ts:690) and its own doc comment calls it "GENERIC by construction". The action vocabulary (AuditAction, :602), the change vocabulary (AuditChangeSlot, :633), the target vocabulary (AuditTargetKind, :611) and the RFC 5737 source-address rule (AuditSourceBlock, :652) all shipped, and Zendesk serves the table today (reader.listAuditEvents(), ruled 2026-09-07). TWO things are missing and neither is the entity: git-host members on AuditTargetKind, and a generation that WRITES personActor — the field is declared for exactly this wave and "this generation writes none" (:695-697)
no project in this universe is a fork and none has been forked: the group owns every project and there is no namespace outside it to fork into — the same fact every project object already reports as forks_count: 0
the project-scoped twin of the group invitation list, held to the same fact: a second group exists from hello-16, and no invitation to this project has ever been made. Reviewed again 2026-09-09 against the subgroup.
these govern who may PUBLISH into a registry; a read-only mirror accepts no publishes, so nothing has ever configured a rule here — and real GitLab answers a project with none configured 200 []
these govern who may PUBLISH into a registry; a read-only mirror accepts no publishes, so nothing has ever configured a rule here — and real GitLab answers a project with none configured 200 []
these govern who may PUBLISH into a registry; a read-only mirror accepts no publishes, so nothing has ever configured a rule here — and real GitLab answers a project with none configured 200 []
these govern who may PUBLISH into a registry; a read-only mirror accepts no publishes, so nothing has ever configured a rule here — and real GitLab answers a project with none configured 200 []
the namespaces a project can be moved into, excluding its current parent. From hello-16 there is one that qualifies - the subgroup - so reader.listNamespaces() answers a real row where the 2026-09-09 ruling had nowhere to point. Older pins still answer the empty list. SERVED 2026-09-09 (hello-16)
SERVED 2026-09-12 (hello-18): APIEntitiesMarkdownUploadAdmin over the files hanging off THIS PROJECT'S OWN RECORDS — its issues, those issues' comments and its merge requests — which is a JOIN rather than a column, because decisions/2026-09-12-1148 declines a `repo` parent in as many words: a row claiming a project upload with no record behind it would be a file nobody uploaded. Newest first, which is the operation's own stated order, tie-broken on the canonical id so the list paginates identically on every request. `size` is the artifact's MEASURED length, computed at compile from the body rather than drawn, so the list and the download cannot describe different files; `uploaded_by` is APIEntitiesUserSafe and every uploader resolves to a person this host serves. THE ALERT METRIC IMAGES ARE DELIBERATELY NOT IN THIS LIST and conformance asserts their absence: GitLab keeps them in its system store (/uploads/-/system/alert_metric_image/…, which is the vendored component's own example path), not among a project's markdown uploads, and serving one file at both addresses would assert two uploads where canon records one. Gated on hasAttachmentParents(); an older artifact answers the named hello-18 upload gap rather than [], because [] there would say nobody here ever attached a file — false of the story every generation tells, where only the PARENT column was missing
SERVED 2026-09-12 (hello-18): the FILE. The vendored operation declares no response schema at all, because the body is the bytes: this route answers the artifact's own attachment.body verbatim, with the artifact's own content_type and `Content-Disposition: attachment` naming the file. The disposition is `attachment` for every kind rather than a branch this host guesses at — GitLab serves an upload inline only for a type on its own safe list, and image/svg+xml, which every image in this canon carries, is explicitly not on it because an SVG can carry script. Conformance re-reads the artifact row and asserts the served bytes are identical to it and that their length is the `size` the list advertised. THE `secret` IS THE FILE'S OWN DIGEST, first 32 characters. GitLab mints a 32-character hex token per upload and canon mints none; an invented one would be a value no client could cross-check, while the first half of attachment.sha256 — measured from the body at compile — is 32 hex characters, is a pure function of the bytes it addresses, and is reproducible by any caller that has downloaded the file. Conformance asserts the secret address and the id address serve the same bytes, and that a secret which is not this file's digest is GitLab's own 404. The filename must match too: a right secret with a wrong name addresses nothing. Gated on hasAttachmentParents(); an older artifact answers the named hello-18 upload gap
SERVED 2026-09-12 (hello-18): the FILE. The vendored operation declares no response schema at all, because the body is the bytes: this route answers the artifact's own attachment.body verbatim, with the artifact's own content_type and `Content-Disposition: attachment` naming the file. The disposition is `attachment` for every kind rather than a branch this host guesses at — GitLab serves an upload inline only for a type on its own safe list, and image/svg+xml, which every image in this canon carries, is explicitly not on it because an SVG can carry script. Conformance re-reads the artifact row and asserts the served bytes are identical to it and that their length is the `size` the list advertised. The id is gitlabId(attachment.id), the same id the list publishes, and an id this project's records do not carry is GitLab's own 404 — a group upload's id included. Gated on hasAttachmentParents(); an older artifact answers the named hello-18 upload gap
the projects a person owns in their OWN namespace. Canon models no personal namespace — the group owns every project — so nobody owns one. users/{id}/contributed_projects answers the question this is often mistaken for, and it is served with real rows. RULED by the founder 2026-09-09; a subgroup and group-level objects are on hello-16
the groups a project can be invited to, excluding the one that already owns it. The subgroup qualifies from hello-16. Unpaginated: the vendored document declares no page/per_page on this operation. Older pins still answer the empty list. SERVED 2026-09-09 (hello-16)
generate — A WIDENING, NOT A BUILD: canon.AdminAuditEvent exists (packages/canon/src/support-desk.ts:690) and its own doc comment calls it "GENERIC by construction". The action vocabulary (AuditAction, :602), the change vocabulary (AuditChangeSlot, :633), the target vocabulary (AuditTargetKind, :611) and the RFC 5737 source-address rule (AuditSourceBlock, :652) all shipped, and Zendesk serves the table today (reader.listAuditEvents(), ruled 2026-09-07). TWO things are missing and neither is the entity: git-host members on AuditTargetKind, and a generation that WRITES personActor — the field is declared for exactly this wave and "this generation writes none" (:695-697)
generate — A WIDENING, NOT A BUILD: canon.AdminAuditEvent exists (packages/canon/src/support-desk.ts:690) and its own doc comment calls it "GENERIC by construction". The action vocabulary (AuditAction, :602), the change vocabulary (AuditChangeSlot, :633), the target vocabulary (AuditTargetKind, :611) and the RFC 5737 source-address rule (AuditSourceBlock, :652) all shipped, and Zendesk serves the table today (reader.listAuditEvents(), ruled 2026-09-07). TWO things are missing and neither is the entity: git-host members on AuditTargetKind, and a generation that WRITES personActor — the field is declared for exactly this wave and "this generation writes none" (:695-697)
refuse — olympus-labs publishes no GitLab Pages site — no project in this universe has a pages deployment or a custom domain; GitLab's own 404 for a project with Pages unused is the whole answer. ALL FOUR ROWS REFUSE, the domain list included: a Pages domain collection is addressable only on a project that HAS Pages, and no project here does — a 200 `[]` there would advertise a Pages site with no custom domain rather than the absence of Pages. The vendored document declares `404 Not Found` on all four operations. UNGATED: canon has no PagesSite in any generation. RULED by the founder 2026-09-12
generate — canon has no SecurityFinding/SbomComponent/Attestation; a scanning org has findings
generate — canon has no Star; the founder ruled stars generated on both hosts so a starred repo is the same repo on GitHub and GitLab
generate — canon has no Star; the founder ruled stars generated on both hosts so a starred repo is the same repo on GitHub and GitLab
SERVED 2026-09-12 (coverage wave W8): GitLab's ResourceStateEvent vocabulary is NARROW — opened, closed, merged, reopened — and canon's `issue_transition` is a WORKFLOW-COLUMN log (backlog, todo, in_progress, in_review, done, canceled). So a column move is NOT a state event and is not served as one: only a move into a terminal column (done or canceled, exactly the two workflow states canon marks an issue `closed` in) is a CLOSE, and a move back out of one would be a REOPEN. `wave-w8.test.ts` walks the whole issue roster and asserts that constant against the artifact, so the day canon grows a third terminal column the row goes red rather than quietly under-reporting a close. `id` is (issue, ord) minted through gitlabId; `user` is the transition's actor, `created_at` its instant, and `source_merge_request_id` the merge request that performed the close where `by_pull` records one and nothing at all where it does not. `source_commit` is omitted — canon records no commit against a close. An OPEN issue has no state event and says so with `[]` rather than a 404: the issue resolves, it simply never changed state. An iid nobody has is GitLab's own 404
SERVED 2026-09-12 (coverage wave W8): GitLab's ResourceStateEvent vocabulary is NARROW — opened, closed, merged, reopened — and canon's `issue_transition` is a WORKFLOW-COLUMN log (backlog, todo, in_progress, in_review, done, canceled). So a column move is NOT a state event and is not served as one: only a move into a terminal column (done or canceled, exactly the two workflow states canon marks an issue `closed` in) is a CLOSE, and a move back out of one would be a REOPEN. `wave-w8.test.ts` walks the whole issue roster and asserts that constant against the artifact, so the day canon grows a third terminal column the row goes red rather than quietly under-reporting a close. `id` is (issue, ord) minted through gitlabId; `user` is the transition's actor, `created_at` its instant, and `source_merge_request_id` the merge request that performed the close where `by_pull` records one and nothing at all where it does not. `source_commit` is omitted — canon records no commit against a close. The same event by id, byte-identical to its entry in the list, which conformance compares directly
SERVED 2026-09-12 (coverage wave W8): canon needs no history table for a merge request's state — one that settled carries the instant it settled at, and that instant IS the one event GitLab records: `merged` for one that landed, `closed` for one that did not, and nothing for one still open. reader.listPulls; `user` is the author, `resource_id` the merge request, `id` minted from (pull, state). An open merge request answers `[]` rather than a 404: it resolves, it simply has not settled. An iid nobody has is GitLab's own 404
SERVED 2026-09-12 (coverage wave W8): canon needs no history table for a merge request's state — one that settled carries the instant it settled at, and that instant IS the one event GitLab records: `merged` for one that landed, `closed` for one that did not, and nothing for one still open. reader.listPulls; `user` is the author, `resource_id` the merge request, `id` minted from (pull, state). The same event by id, byte-identical to its entry in the list
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
SERVED 2026-09-12 (hello-17): the notes inside one epic discussion, byte-identical to the same notes inside the discussion object. The vendored document types this operation's 200 as APIEntitiesDiscussion - Grape's desc block disagreeing with a route that presents the NOTE collection - so conformance validates against APIEntitiesNote, which is what a client receives; the issue and merge-request twins make the same call
SERVED 2026-09-12 (hello-17): one note inside one epic discussion, rendered by the same glEpicNote every other note surface uses
no commit in this universe carries a comment — DECISIONS 2026-08-20 [commit-detail] ruled it per endpoint and that ruling stands (the 2026-08-29 entry superseded only its three-verdict bar); GitLab's own answer for a commit with no notes is `[]` at 200. THE LIST HALF: the commit must resolve first (reader.getCommitBySha), so a sha nobody pushed is still GitLab's 404 rather than a cheerful `[]`. UNGATED: packages/canon/src/notes.ts:52 records that hello-17's entity-note domain may not declare a `commit` parent until this ruling is reversed, so no generation of this universe carries a commit note. Falsifiable: the `expectsEmpty` entry reds the row the day one appears. RULED by the founder 2026-09-12
SERVED 2026-09-12 (hello-17): a discussion IS the epic's notes grouped by entity_note.thread_root - not a second entity and not a second query - so /notes and /discussions partition exactly the same set and render each note identically. individual_note is `this thread has one note`; resolvable is false because GitLab makes only DIFF notes resolvable and an epic has no diff. The vendored component types `notes` as a single object where real GitLab sends an array, so the row is a deviation, exactly as its issue and merge-request twins are
SERVED 2026-09-12 (hello-17): one epic discussion by its 40-hex digest, which is SHA-1 over the canonical id of the thread's root note - the same derivation the issue and merge-request discussions use, so a discussion is addressable by exactly one thread. A discussion that belongs to a different epic 404s here rather than being served under the wrong parent
refuse — no commit in this universe carries a comment — DECISIONS 2026-08-20 [commit-detail] ruled it per endpoint and that ruling stands (the 2026-08-29 entry superseded only its three-verdict bar); GitLab's own answer for a commit with no notes is `[]` at 200. THE DISCUSSION-ID HALF: with no note on any commit there is no discussion id to resolve, so GitLab's own 404 is the whole answer rather than a 200 over a discussion that does not exist. UNGATED, for the reason the list half states. RULED by the founder 2026-09-12
refuse — no commit in this universe carries a comment — DECISIONS 2026-08-20 [commit-detail] ruled it per endpoint and that ruling stands (the 2026-08-29 entry superseded only its three-verdict bar); GitLab's own answer for a commit with no notes is `[]` at 200. THE DISCUSSION-ID HALF: with no note on any commit there is no discussion id to resolve, so GitLab's own 404 is the whole answer rather than a 200 over a discussion that does not exist. UNGATED, for the reason the list half states. RULED by the founder 2026-09-12
refuse — no commit in this universe carries a comment — DECISIONS 2026-08-20 [commit-detail] ruled it per endpoint and that ruling stands (the 2026-08-29 entry superseded only its three-verdict bar); GitLab's own answer for a commit with no notes is `[]` at 200. THE DISCUSSION-ID HALF: with no note on any commit there is no discussion id to resolve, so GitLab's own 404 is the whole answer rather than a 200 over a discussion that does not exist. UNGATED, for the reason the list half states. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
SERVED 2026-09-12 (hello-17): features.socialGraph derives the follow edges from the review graph - you follow the people whose changes you review most and the people who review yours - in ONE person_follow table both git hosts read, which is the cross-host parity this reason has committed to since the classification run. Ordered by the follower's canonical id, the order both hosts page. On every older artifact it answers the named hello-17 generation gap, because [] would assert that nobody here follows anybody
SERVED 2026-09-12 (hello-17): the other end of the same directed edge, off the same person_follow table, ordered by the followee's canonical id. Every edge a person's /followers names is visible from that follower's /following, and no edge names the same person twice
SERVED 2026-09-12 (coverage wave W3): reader.credentialsForPerson(athena, 'email') - athena is the caller GET /api/v4/user reports, and GitHub's /user/emails has been green off the same person_credential rows since the hello-16 credential wave. `confirmed_at` is the credential's own instant and is sent only on an ACTIVE row: GitLab's field records when the address was confirmed to belong to its owner, canon records when the person came to hold it, and a row canon ever revokes loses the field in the same edit rather than keeping a confirmation that is no longer true. `id` is the integer real GitLab sends where the vendored component types it string, so the row is a deviation. Behind the hello-16 credential gate, not the hello-17 caller gate: the caller has held an address since hello-16, which is a different question from whether she holds a key. The old reason - canon's person carries no email list - was falsified by that generation
SERVED 2026-09-12 (coverage wave W3): the by-id twin of /api/v4/user/emails, addressed by gitlabId(credential.id); conformance asserts the list and the by-id row render one address the same way. Same hello-16 gate, same `id` deviation, same falsified reason
nobody in olympus-labs sets a status message — canon's person carries no availability, emoji or status text; GitLab's own answer for a user who has set none is a 200 with every field null. NOT A COLLECTION: `APIEntitiesUserStatus` is an object, so the honest empty is the all-null object rather than `[]`. All five fields are a `deviation` against the vendored component, which types each of them `string` with no `nullable` while GitLab renders `Entities::UserStatus` over a person who has set no status and every field comes out nil. UNGATED: `canon.Person` (packages/canon/src/entities.ts:79) has carried id, archetype, teams, utcOffsetMinutes, isBot and member in every generation and has grown only `aiTool`/`extraAiTools` since, so no artifact of this universe has ever held an availability, an emoji or a status text. RULED by the founder 2026-09-12
nobody in olympus-labs sets a status message — canon's person carries no availability, emoji or status text; GitLab's own answer for a user who has set none is a 200 with every field null. NOT A COLLECTION: `APIEntitiesUserStatus` is an object, so the honest empty is the all-null object rather than `[]`. All five fields are a `deviation` against the vendored component, which types each of them `string` with no `nullable` while GitLab renders `Entities::UserStatus` over a person who has set no status and every field comes out nil. UNGATED: `canon.Person` (packages/canon/src/entities.ts:79) has carried id, archetype, teams, utcOffsetMinutes, isBot and member in every generation and has grown only `aiTool`/`extraAiTools` since, so no artifact of this universe has ever held an availability, an emoji or a status text. RULED by the founder 2026-09-12
refuse — GitLab restricts the instance-wide last-activity index to administrators
generate — canon's person carries no email list, preferences or status message
refuse — a support PIN authenticates a human to GitLab Support; SandboxAPIs has no support desk and issuing one would be a credential-shaped lie
refuse — GitLab restricts another user's addresses to instance administrators and this host authenticates as an ordinary member of one group, so GitLab's own 403 is the whole answer. THE OLD REASON WAS FALSIFIED RATHER THAN UPHELD: it said canon's person carries no email list, and hello-16 gave it one — person_credential kind `email`, which /api/v4/user/emails serves today — so the blocker is the TOKEN, not the canon, which is the same reading that puts the whole /api/v4/admin/* family in class 1 of the 2026-09-01 wave. /api/v4/user/emails is deliberately untouched and still served: a caller reading their OWN addresses needs no administrator, and that distinction is what this row turns on. UNGATED: no generation of this universe authenticates as an instance administrator. RULED by the founder 2026-09-12 (round 20)
refuse — a support PIN authenticates a human to GitLab Support; SandboxAPIs has no support desk and issuing one would be a credential-shaped lie
content_reaction has carried target_kind 'epic' since hello-15; the block was that this renderer served no epic route, and hello-16's portfolio layer gives the epic one. reactionsFor('epic', id) under a resolvable epic_iid. Two gates in the order a caller meets them: no epic before hello-16 (noEpics), no reactions before hello-15 (noAwardEmoji). SERVED 2026-09-09 (hello-16)
content_reaction has carried target_kind 'epic' since hello-15; the block was that this renderer served no epic route, and hello-16's portfolio layer gives the epic one. reactionsFor('epic', id) under a resolvable epic_iid. Two gates in the order a caller meets them: no epic before hello-16 (noEpics), no reactions before hello-15 (noAwardEmoji). SERVED 2026-09-09 (hello-16)
ContentReaction is canon as of hello-15 (features.contentReactions): one row per person per gesture per target, with an id, a discriminated target and an instant. reactionsFor() answers it and renderer-gitlab's glAwardEmoji renders GitLab's AwardEmoji envelope over it - the same rows GitHub serves as reactions and Azure DevOps as comment reactions
ContentReaction is canon as of hello-15 (features.contentReactions): one row per person per gesture per target, with an id, a discriminated target and an instant. reactionsFor() answers it and renderer-gitlab's glAwardEmoji renders GitLab's AwardEmoji envelope over it - the same rows GitHub serves as reactions and Azure DevOps as comment reactions
ContentReaction is canon as of hello-15 (features.contentReactions): one row per person per gesture per target, with an id, a discriminated target and an instant. reactionsFor() answers it and renderer-gitlab's glAwardEmoji renders GitLab's AwardEmoji envelope over it - the same rows GitHub serves as reactions and Azure DevOps as comment reactions
ContentReaction is canon as of hello-15 (features.contentReactions): one row per person per gesture per target, with an id, a discriminated target and an instant. reactionsFor() answers it and renderer-gitlab's glAwardEmoji renders GitLab's AwardEmoji envelope over it - the same rows GitHub serves as reactions and Azure DevOps as comment reactions
ContentReaction is canon as of hello-15 (features.contentReactions): one row per person per gesture per target, with an id, a discriminated target and an instant. reactionsFor() answers it and renderer-gitlab's glAwardEmoji renders GitLab's AwardEmoji envelope over it - the same rows GitHub serves as reactions and Azure DevOps as comment reactions
ContentReaction is canon as of hello-15 (features.contentReactions): one row per person per gesture per target, with an id, a discriminated target and an instant. reactionsFor() answers it and renderer-gitlab's glAwardEmoji renders GitLab's AwardEmoji envelope over it - the same rows GitHub serves as reactions and Azure DevOps as comment reactions
ContentReaction is canon as of hello-15 (features.contentReactions): one row per person per gesture per target, with an id, a discriminated target and an instant. reactionsFor() answers it and renderer-gitlab's glAwardEmoji renders GitLab's AwardEmoji envelope over it - the same rows GitHub serves as reactions and Azure DevOps as comment reactions
ContentReaction is canon as of hello-15 (features.contentReactions): one row per person per gesture per target, with an id, a discriminated target and an instant. reactionsFor() answers it and renderer-gitlab's glAwardEmoji renders GitLab's AwardEmoji envelope over it - the same rows GitHub serves as reactions and Azure DevOps as comment reactions
generate — HALF UNBLOCKED 2026-09-12 (hello-17): the epic-COMMENT entity this reason used to name EXISTS now (features.entityNotes) and this renderer serves all six epic note and discussion routes, so a note_id resolves. What is still missing is the AWARDABLE: content_reaction carries target_kind values for issues, comments, pulls, review comments, releases and epics, and nothing in the reaction pass attaches a reaction to an epic NOTE. [] here would assert that nobody reacted to this sentence, which is a claim about the people rather than about the artifact, so the row keeps the coverage 404. THE ASK: an `entityNote` awardable kind in the reaction pass, and this row plus its by-id twin go green with no new entity
generate — HALF UNBLOCKED 2026-09-12 (hello-17): the by-id twin of the epic-note award list, and blocked by the same single fact - the note exists and resolves as of this wave, the reaction on it does not, because no content_reaction row carries an epic note as its target. THE ASK is the list row's
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
SERVED 2026-09-12 (coverage wave W7): a real archive of the ref's tree, whose members are `reader.treeFiles(commit)` - the same content-addressed blobs `/repository/files/{path}/raw`, `/repository/blobs/{sha}/raw` and `git clone` serve, so a file cannot have one body here and another there. GITLAB'S OWN NAMING, not GitHub's: the single root directory and the download filename are `{project path}-{ref}-{full sha}` from `archive_prefix` (the project's own path segment, the ref as the caller wrote it with slashes flattened, and the sha appended unconditionally because the API passes `append_sha: true`). FORMATS: `tar.gz` (the default - `format ||= 'tar.gz'`), `tar` and `zip` are served, and the three gzip spellings `tgz`/`gz` and any unrecognised `?format=` collapse onto tar.gz exactly as `archive_file_path` does. The five bzip2 spellings answer the coverage 404 naming the format and the reason - node:zlib carries no bzip2 codec and the shared writer has no dependencies - rather than a gzip wearing a `.tar.bz2` name, which is what falling back to the default would have done. A suffix outside GitLab's own `archive_formats_regex` gets GitLab's plain 404, since its route does not match either. `sha` and `ref_type` are answered; `include_lfs_blobs` is answered too and conformance asserts the two values select byte-identical archives, which is true because the file model holds whole file bodies and no LFS pointer; `path` and `exclude_paths` are refused, because a caller who asked for a subfolder and got the whole repository has been answered a different question. Unblocked by the tar/zip writers moving out of packages/renderer-github into `@sandboxapis/archive`, founder-ordered 2026-09-09
refuse — GitLab renders its changelog template over the commits carrying a `Changelog:` git trailer. Canon writes no trailers — every commit this host serves reports trailers {} — so no commit is ever selectable and there is no changelog to generate; the documented 400 is the refusal. RULED by the founder 2026-09-09
refuse — this reports Gitaly storage verification for a physical repository; the artifact has no Gitaly behind it
SERVED 2026-09-12 (hello-17): features.entityNotes gives a plan item the planning conversation the storyline already had - entity_note rows keyed to an epic, authored by the people who commented on the issues underneath it. listEntityNotes() answers this list in canon id order, which is chronological within a parent. On every older artifact it answers the named hello-17 generation gap and never [], because an empty note list asserts that this organization plans in silence
SERVED 2026-09-12 (hello-17): one epic note by id, rendered by the same glEpicNote the list and the discussions use, so the three surfaces cannot describe one sentence three ways
refuse — olympus-labs documents its services in the parthenon repository, not in a wiki — no WikiPage exists at project or group scope; GitLab answers `[]` at 200 for the wiki list and its own 404 for a slug that names nothing. THE SLUG/PAGE-ID HALF: no slug and no wiki_page_meta_id resolves, so GitLab's own 404 is the whole answer — a 200 `[]` on a wiki page's note list would assert that the page exists and nobody commented on it. UNGATED: canon has no WikiPage in any generation. RULED by the founder 2026-09-12
refuse — olympus-labs documents its services in the parthenon repository, not in a wiki — no WikiPage exists at project or group scope; GitLab answers `[]` at 200 for the wiki list and its own 404 for a slug that names nothing. THE SLUG/PAGE-ID HALF: no slug and no wiki_page_meta_id resolves, so GitLab's own 404 is the whole answer — a 200 `[]` on a wiki page's note list would assert that the page exists and nobody commented on it. UNGATED: canon has no WikiPage in any generation. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no vulnerability exists in this organization — canon carries no SecurityFinding, so no vulnerability id resolves, and GitLab's own 404 for an unknown vulnerability is the whole answer. BOTH HALVES REFUSE rather than one of them answering `[]`: the note list hangs off a `{noteable_id}` that names a VULNERABILITY, and a 200 `[]` there would assert the vulnerability exists and nobody commented on it. UNGATED: packages/canon/src/notes.ts:52 records `SecurityFinding` as the entity hello-17's entity-note domain is still missing, so no generation of this universe holds one. RULED by the founder 2026-09-12
refuse — no vulnerability exists in this organization — canon carries no SecurityFinding, so no vulnerability id resolves, and GitLab's own 404 for an unknown vulnerability is the whole answer. BOTH HALVES REFUSE rather than one of them answering `[]`: the note list hangs off a `{noteable_id}` that names a VULNERABILITY, and a 200 `[]` there would assert the vulnerability exists and nobody commented on it. UNGATED: packages/canon/src/notes.ts:52 records `SecurityFinding` as the entity hello-17's entity-note domain is still missing, so no generation of this universe holds one. RULED by the founder 2026-09-12
refuse — olympus-labs documents its services in the parthenon repository, not in a wiki — no WikiPage exists at project or group scope; GitLab answers `[]` at 200 for the wiki list and its own 404 for a slug that names nothing. THE SLUG/PAGE-ID HALF: no slug and no wiki_page_meta_id resolves, so GitLab's own 404 is the whole answer — a 200 `[]` on a wiki page's note list would assert that the page exists and nobody commented on it. UNGATED: canon has no WikiPage in any generation. RULED by the founder 2026-09-12
refuse — olympus-labs documents its services in the parthenon repository, not in a wiki — no WikiPage exists at project or group scope; GitLab answers `[]` at 200 for the wiki list and its own 404 for a slug that names nothing. THE SLUG/PAGE-ID HALF: no slug and no wiki_page_meta_id resolves, so GitLab's own 404 is the whole answer — a 200 `[]` on a wiki page's note list would assert that the page exists and nobody commented on it. UNGATED: canon has no WikiPage in any generation. RULED by the founder 2026-09-12
EpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)
EpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)
EpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)
the CHILD epics of an epic - GitLab's Epics::EpicLinks, which the vendored summary labels "related epics" while its POST twin says "relate epic to a PARENT". epic.parent/level are canon from hello-16 (features.portfolio) and reader.epicChildren(id) answers it directly; a leaf reports the empty array and an older pin the noEpics generation-gap 404. The 2026-09-01 reason was right that parentage was not modelled and is no longer true; the SIBLING relation it also names still is not, which is why groups/{id}/epics/{epic_iid}/related_epics stays generate. SERVED 2026-09-09 (hello-16)
EpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)
EpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)
EpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
generate — STILL BLOCKED 2026-09-12 (hello-17): IssueLink is canon now and the four issue-to-issue rows serve, but this operation is EPIC-to-EPIC and issue_link's source and target are both issue ids. The one epic relation canon carries is PARENTAGE (features.portfolio's level and parent), which GitLab serves at /epics/{epic_iid}/epics and is a different relation from the sibling `related_epics` set. THE ASK: an epic-to-epic link kind, or a widening of issue_link's ends to a discriminated parent
generate — STILL BLOCKED 2026-09-12 (hello-17): the group-wide list of the same epic-to-epic relation /epics/{epic_iid}/related_epics enumerates per epic, and blocked by the same single fact - issue_link links two ISSUES, and canon's only epic-to-epic relation is parentage. THE ASK is that row's
SERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): reader.listWorkflowSchedules(repo) over canon.WorkflowSchedule. `description` and `ref` are the schedule's own description and branch, `owner` is the actor it runs as, `created_at` and `updated_at` its two epochs, `cron_timezone` UTC because canon's hours ARE UTC, and `cron` is the (per_hour, hours_of_day, days_of_week) triple re-encoded LOSSLESSLY — every value in the triple appears in the expression and nothing else does, which conformance checks field by field rather than against a literal. `active` is true because every schedule here really starts runs, and an inactive schedule that had started 398 pipelines would contradict its own pipeline list. `next_run_at` is walked forward from THE ARTIFACT'S ANCHOR, never Date.now(): a frozen pin must print the same instant in a year's time. `inputs` and `variables` are omitted — canon carries no schedule variable of any kind. GENERATION-GATED: an older artifact answers the CI-configuration gap rather than `[]`, which would say nothing here runs unattended. `scope=inactive` is a real filter that truthfully matches nothing, the posture `pipelines?status=` takes for a value this generator never produces
SERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): the runs one schedule started — reader.runsForSchedule(id), reading `workflow_run.schedule` directly. hello-15's schedule starts REAL runs carrying event `schedule`, and they drive the RELEASE workflow while `/projects/{id}/pipelines` lists the `ci` one — so they are deliberately NOT in that feed, and the 2026-09-09 reason that said they were is corrected here. WHAT WOULD HAVE BEEN AN INVARIANT-#5 BREAK IS NAMING A PIPELINE THIS HOST THEN REFUSES TO FETCH, and this wave closed it: `findPipeline` resolves ANY run of the project rather than only a `ci` one, so every id in this list answers at `/pipelines/{pipeline_id}` and its jobs answer too, which conformance drives row by row. The LIST is deliberately left alone — widening it to all 398 runs would move bytes every frozen pin already serves. Generation-gated with the rest of the family
the nightly release's publishing credential, off reader.scheduleCiBindings(schedule) — a job that runs unattended cannot be handed its credential by a person, which is why the binding is stored on the schedule rather than pasted into a run. THE SAME BYTES ITS OWN RUNS PUBLISH: conformance compares this response to /pipelines/{pipeline_id}/variables on a run this schedule started, so one fact cannot be answered two ways. GitLab's own `404 Not found` for an unknown schedule or key, `%ZZ` included. SERVED 2026-09-12 (hello-19 `ciBindings`): the 2026-09-09 re-scope asked for a variable table and named the constraint that would make one honest — a masked value is a secret canon stores none of. hello-19's `ci_binding` is that table, built on exactly that constraint: one row, one `masked` boolean, and NO VALUE IN ANY COLUMN on a masked row (`compiler/src/hello19.test.ts` asserts `COUNT(*) WHERE masked = 1 AND value IS NOT NULL` is 0 on every compiled artifact). A MASKED BINDING RENDERS `hidden: true` AND SHIPS NO `value` KEY, which is GitLab's own vocabulary rather than a convenience: `masked` means blanked out of JOB LOGS and `hidden` (created as `masked_and_hidden`) means THE API NEVER RETURNS THE VALUE, so `masked: true, hidden: false` with the key dropped would be a shape real GitLab never sends. An unmasked binding's `value` is the artifact row `ci_binding.value_source` names — the collector's endpoint, the default branch, the orb document's tree path — resolved at compile time, so the setting and the thing it configures cannot drift apart. `variable_type` is `env_var` on every row, which agrees with /api/v4/projects/{id}/secure_files being ruled empty: a `file` variable IS a secure file. `raw` and `description` are OMITTED rather than defaulted — neither is required by APIEntitiesCiVariable and neither is a fact canon holds (the depth-hooks.ts rule, one wave earlier). Conformance drives each row against the vendored schema AND against the reader accessor the renderer must have used, walks the pages with the headers asserted, and fails if a masked row ever carries a value. Gated on hasCiBindings(); an older artifact answers the named hello-19 CI-variable gap, because [] there would say this organization configures its pipelines with nothing while the same artifact publishes a CI document, a nightly schedule and deployments to two environments
a bridge is a job that triggers a downstream pipeline; this universe's CI is a single project's pipeline with no downstream trigger, so the bridge list is truthfully empty
GitLab's other name for the same thing the bridges row carries, held to the same fact: this CI triggers no downstream pipeline
the variables ONE RUN WAS STARTED WITH — a genuinely two-branch answer, and `workflow_run.schedule` is the column that tells the branches apart without a join. A run a schedule started carries that schedule's bindings (reader.scheduleCiBindings); a run somebody pushed was started with none and answers [] at 200, which is the true answer rather than a gap. 86 of this universe's 350 runs at 90 days carry a schedule, so conformance drives both branches and fails if either becomes unreachable. The pipeline id resolves FIRST, on every generation: an id this project does not have is GitLab's own `404 Not found`. SERVED 2026-09-12 (hello-19 `ciBindings`): the 2026-09-09 re-scope asked for a variable table and named the constraint that would make one honest — a masked value is a secret canon stores none of. hello-19's `ci_binding` is that table, built on exactly that constraint: one row, one `masked` boolean, and NO VALUE IN ANY COLUMN on a masked row (`compiler/src/hello19.test.ts` asserts `COUNT(*) WHERE masked = 1 AND value IS NOT NULL` is 0 on every compiled artifact). A MASKED BINDING RENDERS `hidden: true` AND SHIPS NO `value` KEY, which is GitLab's own vocabulary rather than a convenience: `masked` means blanked out of JOB LOGS and `hidden` (created as `masked_and_hidden`) means THE API NEVER RETURNS THE VALUE, so `masked: true, hidden: false` with the key dropped would be a shape real GitLab never sends. An unmasked binding's `value` is the artifact row `ci_binding.value_source` names — the collector's endpoint, the default branch, the orb document's tree path — resolved at compile time, so the setting and the thing it configures cannot drift apart. `variable_type` is `env_var` on every row, which agrees with /api/v4/projects/{id}/secure_files being ruled empty: a `file` variable IS a secure file. `raw` and `description` are OMITTED rather than defaulted — neither is required by APIEntitiesCiVariable and neither is a fact canon holds (the depth-hooks.ts rule, one wave earlier). Conformance drives each row against the vendored schema AND against the reader accessor the renderer must have used, walks the pages with the headers asserted, and fails if a masked row ever carries a value. Gated on hasCiBindings(); an older artifact answers the named hello-19 CI-variable gap, because [] there would say this organization configures its pipelines with nothing while the same artifact publishes a CI document, a nightly schedule and deployments to two environments
SERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): reader.listWorkflowSchedules(repo) over canon.WorkflowSchedule. `description` and `ref` are the schedule's own description and branch, `owner` is the actor it runs as, `created_at` and `updated_at` its two epochs, `cron_timezone` UTC because canon's hours ARE UTC, and `cron` is the (per_hour, hours_of_day, days_of_week) triple re-encoded LOSSLESSLY — every value in the triple appears in the expression and nothing else does, which conformance checks field by field rather than against a literal. `active` is true because every schedule here really starts runs, and an inactive schedule that had started 398 pipelines would contradict its own pipeline list. `next_run_at` is walked forward from THE ARTIFACT'S ANCHOR, never Date.now(): a frozen pin must print the same instant in a year's time. `inputs` is omitted — canon carries no schedule input. GENERATION-GATED: an older artifact answers the CI-configuration gap rather than `[]`, which would say nothing here runs unattended. The detail shape adds `last_pipeline`, and it is the newest run THIS SCHEDULE started (reader.runsForSchedule) rather than the project's newest pipeline — conformance asserts every other field is byte-identical to the list row's. VARIABLES SERVED 2026-09-13 (hello-19 `ciBindings`), AND THIS ROW IS A `deviation` BECAUSE OF THEM: the detail shape also publishes the schedule's CI/CD variables, off the SAME glScheduleVariables the by-key row /pipeline_schedules/{id}/variables/{key} reads, so the two surfaces cannot disagree about one fact — conformance compares the block to that URL's bytes element for element. A masked binding renders `masked: true, hidden: true` and ships NO `value` key, here as everywhere: no ci_binding row carries a value when masked = 1 (compiler/src/hello19.test.ts asserts it as SQL over every compiled artifact). THE DEVIATION IS THE VENDORED DOCUMENT'S: APIEntitiesCiPipelineScheduleDetails types `variables` as a SINGLE APIEntitiesCiVariable object while real GitLab sends an ARRAY — `Ci::PipelineSchedule has_many :variables`, `Entities::Ci::PipelineScheduleDetails` exposes them `using: Entities::Ci::Variable` over that association, and GitLab's own published example for this operation shows `"variables": [{...}]`. Invariant #2 mirrors the provider rather than its documentation, so the array is served and the one validator error is declared a spec bug (SCHEDULE_VARIABLES_SPEC_BUGS); the vendored file is upstream-verbatim and sha256-pinned, so it is NOT edited — the correction and the evidence live in conformance and in coverage/provider-pins.yaml. Same class as `approved_by` on the code-review component. The `variables` KEY IS ABSENT rather than `[]` on a pre-hello-19 artifact: [] would say this organization hands its nightly release nothing while the same artifact publishes a CI document, a schedule that starts real runs and deployments to two environments, and a whole-response coverage 404 would break invariant #2 on all 127 frozen pins, which fetch this row and get GitLab's 200. gitlab/hello19.test.ts asserts the row gains exactly that one key and nothing else
SERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): reader.testResultsForJob(job) over canon.TestCase/TestResult, folded PER JOB — GitLab keys a test report's suites by the BUILD that reported them, which is what makes one suite per job the mirror rather than a choice. canon spells a failure "failure" and GitLab spells it "failed"; the other three statuses agree. `system_output` is the reporter's message and is absent on a pass, because canon stores NULL there rather than an empty string. `file`, `stack_trace`, `recent_failures` and `attachment_url` are omitted — canon records none of them. The durations are FLOAT seconds as real GitLab sends them while both vendored components type them `integer`, so the row is a `deviation` rather than rounding canon's millisecond durations away. Generation-gated: an older artifact answers the CI-configuration gap rather than a report of no tests. Conformance recomputes every counter from the artifact and asserts each served case is one canon DECLARES, with canon's own classname — a test name invented here would be a test nobody wrote
SERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): reader.testResultsForJob(job) over canon.TestCase/TestResult, folded PER JOB — GitLab keys a test report's suites by the BUILD that reported them, which is what makes one suite per job the mirror rather than a choice. canon spells a failure "failure" and GitLab spells it "failed"; the other three statuses agree. `system_output` is the reporter's message and is absent on a pass, because canon stores NULL there rather than an empty string. `file`, `stack_trace`, `recent_failures` and `attachment_url` are omitted — canon records none of them. The durations are FLOAT seconds as real GitLab sends them while both vendored components type them `integer`, so the row is a `deviation` rather than rounding canon's millisecond durations away. Generation-gated: an older artifact answers the CI-configuration gap rather than a report of no tests. The SAME fold, totals only: the summary carries `build_ids` and no `test_cases`, which is the whole difference between the two operations and the reason both exist. Conformance asserts the two endpoints agree counter for counter — two different totals for one pipeline would be this host describing two universes of tests. `test_suites` is a `deviation`, the vendored component typing the collection as one object
SERVED 2026-09-12 (coverage wave W3): Project.repository's rootRef, branchCount, tagCount and branchNames(searchPattern,offset,limit) - which is the WHOLE of GitLab's branch surface in GraphQL: its schema publishes no branch object, and mirroring that rather than inventing a richer type is invariant #2. Tags are reached the way GitLab reaches them, through Release. The counters are asserted equal to what `/repository/branches` and `/repository/tags` list
SERVED 2026-09-12 (coverage wave W3): Project.repository.commit(ref) and commits(ref, first, after). Every field is shapeCommit's own value for the same row, so a client cannot read one sha, title or date here and another from `/repository/commits`; the connection walks the SAME ancestry `/repository/commits?ref_name=` walks rather than every commit in the table. `description` is the message below the title and null for a one-liner, which is GitLab's own reading
SERVED 2026-09-12 (coverage wave W3): Project.label(title) and labels(first, after, title). REST's `name` is GraphQL's `title` and the colours are glLabel's own two strings; `createdAt`/`updatedAt` are OMITTED rather than derived, because canon's label row carries no instant at all and GitLab types both non-null - a subset may omit a field, and inventing a creation date for a label would be a placeholder
SERVED 2026-09-12 (coverage wave W3): Project.milestones(first, after, state, title), every field glMilestone's own value for the same row - including the two dates ArtifactReader.milestoneTimes derives, so GraphQL and `/milestones` cannot print different ones. `projectMilestone` is true and the two group flags false because canon's `milestone.repo` is never null
SERVED 2026-09-12 (coverage wave W3): Project.release(tagName) and releases(first, after), every field glRelease's own value for the same row, with `commit` resolving to the same Commit type the repository serves so a release's tag commit is fetchable from either dialect
SERVED 2026-09-12 (coverage wave W3): Repository.tree(path, recursive, ref) with its Blob and TreeEntry connections, and Repository.blobs(paths, ref) for the CONTENT - the GraphQL twin of `/repository/files/{path}`, asserted byte-equal to it. `flatPath` is computed from the artifact's own trees by git's real rule (a directory whose sub-tree holds exactly one directory collapses into it) rather than copied from `path`. `size`/`rawSize` are omitted: they are GitLab's BigInt scalar, which serialises as a string, and a subset promising Int would hand clients the wrong JSON type. Answers the named hello-4 file generation gap on an artifact without the file model
SERVED 2026-09-12 (coverage wave W3): GitLab's unified issue/epic type: Project.workItems is the project's ISSUES and Group.workItems the group's EPICS, which is what the two roots mean. Every issue field is glIssue's own value including the `closed_epoch ?? created_epoch` derivation of updatedAt, and `confidential: false` is knowable rather than a default - canon models no private issue or epic. On an artifact predating hello-16 the GROUP half answers an explicit generation error in `errors` rather than an empty connection, which would assert that this organization plans nothing (invariant #4's GraphQL clause)
no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares (`page`/`per_page`). UNGATED: canon has no `Snippet` entity in any generation — packages/canon/src/avatars-reactions.ts:188 names it as the blocker the hello-15 reaction domain could not answer — so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows a snippet. RULED by the founder 2026-09-12
no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares (`page`/`per_page`). UNGATED: canon has no `Snippet` entity in any generation — packages/canon/src/avatars-reactions.ts:188 names it as the blocker the hello-15 reaction domain could not answer — so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows a snippet. RULED by the founder 2026-09-12
no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares (`page`/`per_page`). UNGATED: canon has no `Snippet` entity in any generation — packages/canon/src/avatars-reactions.ts:188 names it as the blocker the hello-15 reaction domain could not answer — so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows a snippet. RULED by the founder 2026-09-12
no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares (`page`/`per_page`). UNGATED: canon has no `Snippet` entity in any generation — packages/canon/src/avatars-reactions.ts:188 names it as the blocker the hello-15 reaction domain could not answer — so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows a snippet. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — GitLab exposes user-agent detail to instance administrators for abuse reports only; a non-admin token gets 403
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no snippet exists in this organization — every shared file in olympus-labs lives in the parthenon repository and no persona has ever posted a snippet; GitLab answers `[]` at 200 for a snippet list and its own 404 for a snippet id that names nothing. THE ID HALF: no snippet id resolves, so this operation never reaches a collection to be empty of and GitLab's own 404 is the whole answer — `[]` on a snippet's award-emoji or note list would assert that the snippet exists and holds nothing. UNGATED: canon has no `Snippet` in any generation, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — GitLab exposes user-agent detail to instance administrators for abuse reports only; a non-admin token gets 403
refuse — mirroring GitLab MLflow means simulating a second ML platform with no bearing on the dev-tool story; GitLab 404s these for a project with no experiments
refuse — mirroring GitLab MLflow means simulating a second ML platform with no bearing on the dev-tool story; GitLab 404s these for a project with no experiments
refuse — mirroring GitLab MLflow means simulating a second ML platform with no bearing on the dev-tool story; GitLab 404s these for a project with no experiments
refuse — mirroring GitLab MLflow means simulating a second ML platform with no bearing on the dev-tool story; GitLab 404s these for a project with no experiments
refuse — mirroring GitLab MLflow means simulating a second ML platform with no bearing on the dev-tool story; GitLab 404s these for a project with no experiments
refuse — mirroring GitLab MLflow means simulating a second ML platform with no bearing on the dev-tool story; GitLab 404s these for a project with no experiments
refuse — mirroring GitLab MLflow means simulating a second ML platform with no bearing on the dev-tool story; GitLab 404s these for a project with no experiments
refuse — mirroring GitLab MLflow means simulating a second ML platform with no bearing on the dev-tool story; GitLab 404s these for a project with no experiments
refuse — mirroring GitLab MLflow means simulating a second ML platform with no bearing on the dev-tool story; GitLab 404s these for a project with no experiments
refuse — mirroring GitLab MLflow means simulating a second ML platform with no bearing on the dev-tool story; GitLab 404s these for a project with no experiments
refuse — mirroring GitLab MLflow means simulating a second ML platform with no bearing on the dev-tool story; GitLab 404s these for a project with no experiments
generate — canon's jobs ran on nothing; needs a Runner the job table can point at
generate — canon's jobs ran on nothing; needs a Runner the job table can point at
refuse — runner managers, controllers, tokens and router discovery describe physical CI machines and their credentials, not the org; GitLab restricts them to admins
refuse — runner managers, controllers, tokens and router discovery describe physical CI machines and their credentials, not the org; GitLab restricts them to admins
generate — canon's jobs ran on nothing; needs a Runner the job table can point at
generate — canon's jobs ran on nothing; needs a Runner the job table can point at
generate — canon's jobs ran on nothing; needs a Runner the job table can point at
refuse — runner managers, controllers, tokens and router discovery describe physical CI machines and their credentials, not the org; GitLab restricts them to admins
generate — canon's jobs ran on nothing; needs a Runner the job table can point at
refuse — runner managers, controllers, tokens and router discovery describe physical CI machines and their credentials, not the org; GitLab restricts them to admins
refuse — runner managers, controllers, tokens and router discovery describe physical CI machines and their credentials, not the org; GitLab restricts them to admins
the group milestone RESOLVES from hello-16 and nothing is assigned to it: it is the release train the repository's own milestone rolls up into, and all 24 issues that carry a milestone carry the repository's. Served as GitLab's 200 [] under a milestone that exists, rather than the 404 it was when no group milestone could exist - which is still the answer on every older pin. SERVED 2026-09-09 (hello-16)
the merge-request twin of the group milestone's issue list, empty for the same reason: both merge requests that carry a milestone carry the REPOSITORY's, which projects/{id}/milestones/{id}/merge_requests serves with real rows. 200 [] under a milestone that exists from hello-16; the 404 on every older pin. SERVED 2026-09-09 (hello-16)
features.namespaces (hello-16) puts the release train the repository's milestone rolls up into on the subgroup; reader.milestonesForNamespace(id) derives it with group_id and its own iid. Older pins answer the empty list. SERVED 2026-09-09 (hello-16)
the by-id twin of the group milestone list, off the same reader.milestonesForNamespace row; an id nobody holds is still GitLab's 404 Milestone Not Found, and that is the whole answer on every pin older than hello-16. SERVED 2026-09-09 (hello-16)
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
generate — canon keeps current label/milestone state, not its history; needs a ResourceEvent log
no commit in this universe carries a comment — DECISIONS 2026-08-20 [commit-detail] ruled it per endpoint and that ruling stands (the 2026-08-29 entry superseded only its three-verdict bar); GitLab's own answer for a commit with no notes is `[]` at 200. THE LIST HALF: the commit must resolve first (reader.getCommitBySha), so a sha nobody pushed is still GitLab's 404 rather than a cheerful `[]`. UNGATED: packages/canon/src/notes.ts:52 records that hello-17's entity-note domain may not declare a `commit` parent until this ruling is reversed, so no generation of this universe carries a commit note. Falsifiable: the `expectsEmpty` entry reds the row the day one appears. RULED by the founder 2026-09-12
refuse — no commit or tag in this universe is GPG-signed, and GitLab answers its own 404 'GPG Signature Not Found' for an unsigned object
generate — canon has AgentSession but no Duo-shaped workflow, checkpoint or trace projection
generate — canon has AgentSession but no Duo-shaped workflow, checkpoint or trace projection
generate — canon has AgentSession but no Duo-shaped workflow, checkpoint or trace projection
generate — canon has AgentSession but no Duo-shaped workflow, checkpoint or trace projection
generate — canon has AgentSession but no Duo-shaped workflow, checkpoint or trace projection
generate — canon has AgentSession but no Duo-shaped workflow, checkpoint or trace projection
generate — canon has AgentSession but no Duo-shaped workflow, checkpoint or trace projection
generate — canon has AgentSession but no Duo-shaped workflow, checkpoint or trace projection
refuse — a WebSocket upgrade is not an HTTP read and the gateway serves no bidirectional channel
SERVED 2026-09-12 (hello-17): features.issueLinks derives the edges from the storyline - issues in one storyline relate, and an issue whose close precedes another's start blocks it. Canon stores the link ONCE, directed; this route renders it relative to the issue you asked about, so one `blocks` row reads `blocks` from the blocker's end and `is_blocked_by` from the blocked one's. The other end is the same issue /issues/{iid} serves, shaped by the same glIssue, plus the four link fields APIEntitiesRelatedIssue adds; sorted by the relationship's own instant ascending, which is the vendored document's own ordering. Answers the named hello-17 generation gap on every older artifact
SERVED 2026-09-12 (hello-17): one link by id, naming both ends outright - so link_type here is the CANONICAL direction rather than the relative one /links prints. Looked up inside this issue's own links, so a link between two other issues 404s rather than being served under a parent it does not touch
no persona in olympus-labs logs time against an issue or a merge request — canon records no TimeEntry; GitLab's own answer for an untracked issue is a 200 carrying `time_estimate: 0`, `total_time_spent: 0` and both human_ fields null. NOT A COLLECTION: the operation returns `APIEntitiesIssuableTimeStats`, an object, so the honest empty is the zeroed object rather than `[]`. It is the SAME object `glIssue` and `glMergeRequest` already embed as `time_stats` (`emptyTimeStats` in packages/renderer-gitlab/src/render2.ts), read from one function so the standalone endpoint and the embedded block can never disagree (invariant #5). canon's `Issue.estimate` is a story-point estimate on a fibonacci scale rather than a duration, and `time_estimate` is documented in seconds, so mapping one onto the other would be an invention. The two human_ fields are a `deviation` against the vendored component, which types them `string` with no `nullable` while real GitLab renders nil for a zero — the same two entries NULLABLE_SPEC_BUGS already carries for the embedded block. UNGATED: no generation of this universe records a time entry. RULED by the founder 2026-09-12
refuse — GitLab exposes user-agent detail to instance administrators for abuse reports only; a non-admin token gets 403
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
generate — canon has sprint/epic/workflow state but no Board; the board layout itself must be generated
SERVED 2026-09-12 (hello-18): the hooks a group publishes to, off reader.orgWebhooks for the organization and reader.namespaceWebhooks for the subgroup. BOTH CONTAINER IDS ANSWER IT — the vendored document restricts the operation to no particular group, so the subgroup is not denied a family this host lists it under (invariant #5) — and what each holds is a different fact: the organization's is the account-wide feed with all six delivery events, and the subgroup's is the hook every real account has one of, wired up once and never fired, because the subgroup owns no repository (founder ruling F1 of 2026-09-09). That one renders with every event flag FALSE and `alert_status: disabled`, which is GitLab's own vocabulary for a hook that will not deliver — APIEntitiesGroupHook carries no `active` property — and conformance asserts it rather than testing the family only where it is busy. The event-name translation is the project row's, plus the three flags only a group can publish (subgroup_events, member_events, project_events), all false. Gated on hasWebhookFilters()
SERVED 2026-09-12 (hello-18): the same group hook by id, byte-identical to the row the list publishes. The id must belong to the container named in the path: the subgroup's hook id is GitLab's 404 on the organization's path and the other way round, which conformance drives from both sides. Gated on hasWebhookFilters()
SERVED 2026-09-12 (hello-18): the hooks this project publishes to, off reader.repoWebhooks — hello-18's features.webhookFilters widens canon's webhook_subscription from PagerDuty's three account scopes to the three a repository host publishes at, so a project hook and a group hook are ONE object at two scopes rather than a new entity. THE EVENT NAMES ARE THIS RENDERER'S TRANSLATION, not canon's: canon's WebhookEventKind is neutral (ref.pushed, change.opened, change.merged, issue.opened, issue.closed, build.completed) because three renderers read that table, and GitLab publishes a BOOLEAN PER FAMILY instead of a list of names — so ref.pushed becomes push_events, change.opened and change.merged both become merge_requests_events (GitLab has ONE merge-request flag), issue.opened and issue.closed both become issues_events, and build.completed becomes job_events, whose own payload GitLab spells `object_kind: build`. Every other flag the vendored component declares is FALSE, and conformance asserts that in both directions so a flag cannot start defaulting to true. `alert_status` carries canon's `active`, because APIEntitiesProjectHook has no `active` property at all and `executable`/`disabled` is GitLab's own vocabulary for whether a hook will fire. `token_present` and `signing_token_present` are FALSE and are sent, because canon states there is no secret; the knobs canon does not hold (enable_ssl_verification, url_variables, branch filters, custom templates and headers, organization_id, name) are OMITTED rather than defaulted. Gated on hasWebhookFilters(); an older artifact answers the named hello-18 webhook gap, because [] there would say this organization wires up nothing
SERVED 2026-09-12 (hello-18): the same hook by id, byte-identical to the row the list publishes — conformance compares them directly. THE ID MUST BELONG TO THIS PROJECT: a hook id from the group's own feed is GitLab's 404 here, which is the same scoping reader.repoWebhooks()/orgWebhooks() enforce one layer down and which conformance drives from both sides. A hook id nobody holds is GitLab's own 404. Gated on hasWebhookFilters()
generate — RE-SCOPED 2026-09-12 by the hello-18 webhook wave, which served the four hook rows beside this one. The old reason ("canon has no Webhook") is now FALSE: canon carries webhook_subscription with the git hosts' three scopes as of hello-18, and this host serves the subscriptions. What is missing is a DELIVERY LOG — one row per attempted POST, with the request it sent and the response it got — which is a second entity hello-18 deliberately did not invent (decisions/2026-09-12-1125). The subscription's last_delivery_epoch/code/status are a fact about the SUBSCRIPTION, published on the subscription by every host in the fleet, and one row is not a log to page. THE ASK: a canon DeliveryAttempt hanging off webhook_subscription, and this row goes green off it
refuse — instance-wide system hooks are admin-only and describe the installation, not the org
refuse — instance-wide system hooks are admin-only and describe the installation, not the org
generate — RE-SCOPED 2026-09-12 by the hello-18 webhook wave, which served the four hook rows beside this one. The old reason ("canon has no Webhook") is now FALSE: canon carries webhook_subscription with the git hosts' three scopes as of hello-18, and this host serves the subscriptions. What is missing is a DELIVERY LOG — one row per attempted POST, with the request it sent and the response it got — which is a second entity hello-18 deliberately did not invent (decisions/2026-09-12-1125). The subscription's last_delivery_epoch/code/status are a fact about the SUBSCRIPTION, published on the subscription by every host in the fleet, and one row is not a log to page. THE ASK: a canon DeliveryAttempt hanging off webhook_subscription, and this row goes green off it
SERVED 2026-09-12 (coverage wave W7): the zip of the files the job uploaded, from `reader.jobArtifacts(job)` + `reader.artifactContent()` - the SAME bytes the Buildkite artifact download and GitHub's `actions/artifacts/{id}/zip` serve for the same canonical job (invariant #5), each member named by `ArtifactRow.path`, which is where the job wrote it. Served INLINE at 200 rather than through GitHub's 302: the vendored document declares `'200': OK` and real GitLab streams the file from the API host through workhorse, so a redirect here would be mirroring the other provider. `Content-Disposition: attachment; filename="artifacts.zip"`, which is the name GitLab stores and serves a job's uploaded archive under. A pre-hello-15 snapshot answers the NAMED generation gap (`ArtifactRow.content` ships with `features.ciConfig`) and not a bare 404, which would say this job uploaded nothing - a claim about the job, and a false one. A job that really has no artifacts still gets GitLab's own `404 Not found`.. THE INSTANCE-SCOPED ADDRESS of that same archive, asserted byte-identical to the project-scoped one. Unlike `/api/v4/job` and `/api/v4/job/allowed_agents` - which are `refuse` because they ask what job the CALLER is running right now, and a frozen universe has no live job - this one names a job by id, and the job it names is one this host serves. Unblocked by the tar/zip writers moving out of packages/renderer-github into `@sandboxapis/archive`, founder-ordered 2026-09-09
SERVED 2026-09-12 (coverage wave W7): the zip of the files the job uploaded, from `reader.jobArtifacts(job)` + `reader.artifactContent()` - the SAME bytes the Buildkite artifact download and GitHub's `actions/artifacts/{id}/zip` serve for the same canonical job (invariant #5), each member named by `ArtifactRow.path`, which is where the job wrote it. Served INLINE at 200 rather than through GitHub's 302: the vendored document declares `'200': OK` and real GitLab streams the file from the API host through workhorse, so a redirect here would be mirroring the other provider. `Content-Disposition: attachment; filename="artifacts.zip"`, which is the name GitLab stores and serves a job's uploaded archive under. A pre-hello-15 snapshot answers the NAMED generation gap (`ArtifactRow.content` ships with `features.ciConfig`) and not a bare 404, which would say this job uploaded nothing - a claim about the job, and a false one. A job that really has no artifacts still gets GitLab's own `404 Not found`. Unblocked by the tar/zip writers moving out of packages/renderer-github into `@sandboxapis/archive`, founder-ordered 2026-09-09
SERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): `artifact.path` is where the job wrote the file relative to the workspace root, so the archive's directory tree is a GROUP-BY over those paths and needs no archive format at all — which is what separates this row from the three artifact DOWNLOAD rows. One entry per directory on the way to a file plus one per file, in path order; `?recursive=` and `?path=` narrow it as the vendored operation declares. `mode` is OMITTED throughout: canon records no POSIX mode for an artifact and "100644" would be this renderer inventing one. A JOB WITH NO ARTIFACT IS A 404 rather than an empty list — GitLab's artifact browser has nothing to browse when the job uploaded no archive, and `[]` would advertise an empty archive that does not exist; conformance drives such a job and asserts the 404. Generation-gated: an older artifact has no `artifact.path` column at all
hello-13's JobLog/JobLogLine ARE the job trace: the endpoint serves reader.jobLogText(job) — the same canonical bytes Bitbucket's step log serves for the same job. Per-test-case results are a different surface (jobs/{id}/artifacts/tree) and stay generate
SERVED 2026-09-12 (coverage wave W7): the zip of the files the job uploaded, from `reader.jobArtifacts(job)` + `reader.artifactContent()` - the SAME bytes the Buildkite artifact download and GitHub's `actions/artifacts/{id}/zip` serve for the same canonical job (invariant #5), each member named by `ArtifactRow.path`, which is where the job wrote it. Served INLINE at 200 rather than through GitHub's 302: the vendored document declares `'200': OK` and real GitLab streams the file from the API host through workhorse, so a redirect here would be mirroring the other provider. `Content-Disposition: attachment; filename="artifacts.zip"`, which is the name GitLab stores and serves a job's uploaded archive under. A pre-hello-15 snapshot answers the NAMED generation gap (`ArtifactRow.content` ships with `features.ciConfig`) and not a bare 404, which would say this job uploaded nothing - a claim about the job, and a false one. A job that really has no artifacts still gets GitLab's own `404 Not found`.. ADDRESSED BY REF AND JOB NAME: the latest successful pipeline on the branch or tag, then the job of that name on it. `ref_name` resolves as a branch or a tag and nothing else, because the document says outright that `HEAD` or `SHA` references are not supported. A missing `job` is Grape's own `{"error": "job is missing"}` at 400 - that envelope and not GitLab's usual `{message}`, because that is what the framework actually sends for a missing required parameter. `search_recent_successful_pipelines` is refused rather than implemented: it widens the search from the latest successful pipeline to `recent` ones, and neither the vendored document nor GitLab's own page says how far back that reaches, so answering it would be answering a different question. Unblocked by the tar/zip writers moving out of packages/renderer-github into `@sandboxapis/archive`, founder-ordered 2026-09-09
refuse — these answer a running runner presenting a CI job token, not an API caller; a frozen universe has no live job
scope-dispatched search over the group's projects. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers the empty result set for every scope, because a group search is a search over its PROJECTS and it owns none (founder ruling F1). A missing or invalid `scope` is still GitLab's own 400 on either id
refuse — Elasticsearch migrations and Zoekt shards describe the instance search cluster, are admin-only, and say nothing about the org
refuse — Elasticsearch migrations and Zoekt shards describe the instance search cluster, are admin-only, and say nothing about the org
refuse — Elasticsearch migrations and Zoekt shards describe the instance search cluster, are admin-only, and say nothing about the org
refuse — Elasticsearch migrations and Zoekt shards describe the instance search cluster, are admin-only, and say nothing about the org
refuse — GitLab restricts semantic search to Duo Enterprise with an embeddings index built over the repository, and olympus-labs holds no Duo subscription — GitLab's own 403 for an unlicensed namespace is the whole answer. 403 RATHER THAN 404, and the status is the ruling: the vendored operation declares both, and the one that matches the condition the reason names is the plan refusal — the project resolves and the caller may read it, what is missing is the subscription. TWO ROUTES, ONE ROW: the manifest template embeds GitLab's dashed and undashed spellings, exactly as /api/v4/projects/{id}/(-/)search does, and both carry the same x-sandboxapis-refusal row key. UNGATED: no generation of this universe holds a Duo subscription or an embeddings index. RULED by the founder 2026-09-12
SERVED 2026-09-12 (coverage wave W8) (hello-16 `refCatalogs`): reader.listGitignoreTemplates() over `license_catalog` (13 rows, bodies included) and `gitignore_template` (163 rows, source included) — the SAME two compile-time tables GitHub's /licenses and /gitignore/templates already serve, which is what makes a licence fetched through either host the same licence (invariant #5). GENERATION-GATED and `[]` is not an option: an artifact without the catalogs answers the template-catalog generation gap, because `[]` here would say GitLab bundles no licence templates — a claim about the HOST rather than about this organization, and a false one. licence: FACTUAL/CC0 — the licence texts are the licences themselves and the ignore-file templates are github/gitignore's CC0-1.0 corpus, the same grant coverage/reviews/2026-09-09-hello-15-stale-reasons-git-hosts.md records for the GitHub twins. `Entities::TemplatesList` is `{key, name}`, and an ignore-file template is its own key: GitLab derives both from the file name, which conformance asserts rather than assumes
SERVED 2026-09-12 (coverage wave W8) (hello-16 `refCatalogs`): reader.gitignoreTemplate(name) over `license_catalog` (13 rows, bodies included) and `gitignore_template` (163 rows, source included) — the SAME two compile-time tables GitHub's /licenses and /gitignore/templates already serve, which is what makes a licence fetched through either host the same licence (invariant #5). GENERATION-GATED and `[]` is not an option: an artifact without the catalogs answers the template-catalog generation gap, because `[]` here would say GitLab bundles no licence templates — a claim about the HOST rather than about this organization, and a false one. licence: FACTUAL/CC0 — the licence texts are the licences themselves and the ignore-file templates are github/gitignore's CC0-1.0 corpus, the same grant coverage/reviews/2026-09-09-hello-15-stale-reasons-git-hosts.md records for the GitHub twins. CASE-SENSITIVE, as upstream's own file names are: `Node` resolves and `node` does not, which is what gitlab.com does and what GitHub's `/gitignore/templates/{name}` does on this same table
SERVED 2026-09-12 (coverage wave W8) (hello-16 `refCatalogs`): reader.listLicenses() over `license_catalog` (13 rows, bodies included) and `gitignore_template` (163 rows, source included) — the SAME two compile-time tables GitHub's /licenses and /gitignore/templates already serve, which is what makes a licence fetched through either host the same licence (invariant #5). GENERATION-GATED and `[]` is not an option: an artifact without the catalogs answers the template-catalog generation gap, because `[]` here would say GitLab bundles no licence templates — a claim about the HOST rather than about this organization, and a false one. licence: FACTUAL/CC0 — the licence texts are the licences themselves and the ignore-file templates are github/gitignore's CC0-1.0 corpus, the same grant coverage/reviews/2026-09-09-hello-15-stale-reasons-git-hosts.md records for the GitHub twins. `popular` IS the catalog's own `featured` flag rather than a second judgement, and `?popular=true` narrows on it. `content` is the stored body VERBATIM — upstream's `[fullname]` placeholder included — because only the single-template operation declares the parameters that expand it. `nickname`, `html_url` and `source_url` are OMITTED: canon's licence row deliberately stores no URL and no nickname (a URL is a host's rendering of the row, not a fact about the licence), GitLab points both URLs at choosealicense.com, which is a link out of the sandbox, and this host publishes no licence page to point at instead
SERVED 2026-09-12 (coverage wave W8) (hello-16 `refCatalogs`): reader.license(key) over `license_catalog` (13 rows, bodies included) and `gitignore_template` (163 rows, source included) — the SAME two compile-time tables GitHub's /licenses and /gitignore/templates already serve, which is what makes a licence fetched through either host the same licence (invariant #5). GENERATION-GATED and `[]` is not an option: an artifact without the catalogs answers the template-catalog generation gap, because `[]` here would say GitLab bundles no licence templates — a claim about the HOST rather than about this organization, and a false one. licence: FACTUAL/CC0 — the licence texts are the licences themselves and the ignore-file templates are github/gitignore's CC0-1.0 corpus, the same grant coverage/reviews/2026-09-09-hello-15-stale-reasons-git-hosts.md records for the GitHub twins. `{name}` is the licence KEY the collection's own `key` field hands a caller, and a key outside the thirteen is GitLab's own 404 rather than a coverage miss — the catalog resolves, this key is simply not in it. The two documented parameters are HONOURED rather than ignored: `project` and `fullname` substitute upstream's `[project]` and `[fullname]` the way LicenseTemplate#resolve! does. `[year]` is deliberately NOT substituted — real GitLab fills it from the wall clock, and a replica whose answer changed with the calendar would stop being byte-reproducible across the pins
generate — canon has no TemplateCatalog; GitLab's bundled Dockerfile/gitignore/CI/license templates are vendor content canon must carry
generate — canon has no TemplateCatalog; GitLab's bundled Dockerfile/gitignore/CI/license templates are vendor content canon must carry
generate — canon has no TemplateCatalog; GitLab's bundled Dockerfile/gitignore/CI/license templates are vendor content canon must carry
generate — canon has no TemplateCatalog; GitLab's bundled Dockerfile/gitignore/CI/license templates are vendor content canon must carry
the group's own CI/CD variables, off reader.orgCiBindings(org, 'pipeline') for the organization's top-level group and reader.namespaceCiBindings(ns) for the subgroup — two scopes, two sets, resolved through the one groupTarget every group family shares. A `selected` org binding is still a group variable here: visibility is GitHub's vocabulary and GitLab has none, a group variable reaching every project in the group — and conformance asserts that the selected set IS every repository this organization has, so the day a second one appears the row reds rather than over-serving. SERVED 2026-09-12 (hello-19 `ciBindings`): the 2026-09-09 re-scope asked for a variable table and named the constraint that would make one honest — a masked value is a secret canon stores none of. hello-19's `ci_binding` is that table, built on exactly that constraint: one row, one `masked` boolean, and NO VALUE IN ANY COLUMN on a masked row (`compiler/src/hello19.test.ts` asserts `COUNT(*) WHERE masked = 1 AND value IS NOT NULL` is 0 on every compiled artifact). A MASKED BINDING RENDERS `hidden: true` AND SHIPS NO `value` KEY, which is GitLab's own vocabulary rather than a convenience: `masked` means blanked out of JOB LOGS and `hidden` (created as `masked_and_hidden`) means THE API NEVER RETURNS THE VALUE, so `masked: true, hidden: false` with the key dropped would be a shape real GitLab never sends. An unmasked binding's `value` is the artifact row `ci_binding.value_source` names — the collector's endpoint, the default branch, the orb document's tree path — resolved at compile time, so the setting and the thing it configures cannot drift apart. `variable_type` is `env_var` on every row, which agrees with /api/v4/projects/{id}/secure_files being ruled empty: a `file` variable IS a secure file. `raw` and `description` are OMITTED rather than defaulted — neither is required by APIEntitiesCiVariable and neither is a fact canon holds (the depth-hooks.ts rule, one wave earlier). Conformance drives each row against the vendored schema AND against the reader accessor the renderer must have used, walks the pages with the headers asserted, and fails if a masked row ever carries a value. Gated on hasCiBindings(); an older artifact answers the named hello-19 CI-variable gap, because [] there would say this organization configures its pipelines with nothing while the same artifact publishes a CI document, a nightly schedule and deployments to two environments
the same group variable by key, byte-identical to the list's own row (conformance compares them), with the operation's own `404 Group Variable Not Found` for a key this scope does not hold — a subgroup's key on the organization's group included, because they are different scopes. NO decodeURIComponent on {key}: Hono has already decoded the segment, so a second decode would mis-match a key containing a percent sign and THROW on a malformed escape — `…/variables/%ZZ` is a key no variable has, answered 404. SERVED 2026-09-12 (hello-19 `ciBindings`): the 2026-09-09 re-scope asked for a variable table and named the constraint that would make one honest — a masked value is a secret canon stores none of. hello-19's `ci_binding` is that table, built on exactly that constraint: one row, one `masked` boolean, and NO VALUE IN ANY COLUMN on a masked row (`compiler/src/hello19.test.ts` asserts `COUNT(*) WHERE masked = 1 AND value IS NOT NULL` is 0 on every compiled artifact). A MASKED BINDING RENDERS `hidden: true` AND SHIPS NO `value` KEY, which is GitLab's own vocabulary rather than a convenience: `masked` means blanked out of JOB LOGS and `hidden` (created as `masked_and_hidden`) means THE API NEVER RETURNS THE VALUE, so `masked: true, hidden: false` with the key dropped would be a shape real GitLab never sends. An unmasked binding's `value` is the artifact row `ci_binding.value_source` names — the collector's endpoint, the default branch, the orb document's tree path — resolved at compile time, so the setting and the thing it configures cannot drift apart. `variable_type` is `env_var` on every row, which agrees with /api/v4/projects/{id}/secure_files being ruled empty: a `file` variable IS a secure file. `raw` and `description` are OMITTED rather than defaulted — neither is required by APIEntitiesCiVariable and neither is a fact canon holds (the depth-hooks.ts rule, one wave earlier). Conformance drives each row against the vendored schema AND against the reader accessor the renderer must have used, walks the pages with the headers asserted, and fails if a masked row ever carries a value. Gated on hasCiBindings(); an older artifact answers the named hello-19 CI-variable gap, because [] there would say this organization configures its pipelines with nothing while the same artifact publishes a CI document, a nightly schedule and deployments to two environments
the project's CI/CD variables, off reader.repoCiBindings(repo, 'pipeline') followed by each deployment environment's rows through reader.environmentCiBindings(repo, name), published under `environment_scope`. THE ENVIRONMENT ROWS BELONG IN THIS LIST because GitLab has no per-environment variable endpoint at all — environment scoping is a FIELD on the project variable, and the by-key operation's own description (if there are multiple variables with the same key, use filter to select the correct environment_scope) is the vendored document describing exactly this case. The group's rows are NOT here — GitLab merges those into a JOB's environment, not into this endpoint — and neither is the coding agent's, which is the audience scoping every ci_binding accessor is written to enforce. SERVED 2026-09-12 (hello-19 `ciBindings`): the 2026-09-09 re-scope asked for a variable table and named the constraint that would make one honest — a masked value is a secret canon stores none of. hello-19's `ci_binding` is that table, built on exactly that constraint: one row, one `masked` boolean, and NO VALUE IN ANY COLUMN on a masked row (`compiler/src/hello19.test.ts` asserts `COUNT(*) WHERE masked = 1 AND value IS NOT NULL` is 0 on every compiled artifact). A MASKED BINDING RENDERS `hidden: true` AND SHIPS NO `value` KEY, which is GitLab's own vocabulary rather than a convenience: `masked` means blanked out of JOB LOGS and `hidden` (created as `masked_and_hidden`) means THE API NEVER RETURNS THE VALUE, so `masked: true, hidden: false` with the key dropped would be a shape real GitLab never sends. An unmasked binding's `value` is the artifact row `ci_binding.value_source` names — the collector's endpoint, the default branch, the orb document's tree path — resolved at compile time, so the setting and the thing it configures cannot drift apart. `variable_type` is `env_var` on every row, which agrees with /api/v4/projects/{id}/secure_files being ruled empty: a `file` variable IS a secure file. `raw` and `description` are OMITTED rather than defaulted — neither is required by APIEntitiesCiVariable and neither is a fact canon holds (the depth-hooks.ts rule, one wave earlier). Conformance drives each row against the vendored schema AND against the reader accessor the renderer must have used, walks the pages with the headers asserted, and fails if a masked row ever carries a value. Gated on hasCiBindings(); an older artifact answers the named hello-19 CI-variable gap, because [] there would say this organization configures its pipelines with nothing while the same artifact publishes a CI document, a nightly schedule and deployments to two environments
the same project variable by key, with `filter[environment_scope]` honoured as a real selector — four of this project's variables share a key with another (DEPLOY_ENVIRONMENT and ENVIRONMENT_DEPLOY_TOKEN, once per environment), which is the case the operation's description tells a caller to use the filter for, and conformance drives every duplicated key through every scope it exists in plus one it does not. GitLab's own `404 Variable Not Found` for an unknown key, `%ZZ` included. SERVED 2026-09-12 (hello-19 `ciBindings`): the 2026-09-09 re-scope asked for a variable table and named the constraint that would make one honest — a masked value is a secret canon stores none of. hello-19's `ci_binding` is that table, built on exactly that constraint: one row, one `masked` boolean, and NO VALUE IN ANY COLUMN on a masked row (`compiler/src/hello19.test.ts` asserts `COUNT(*) WHERE masked = 1 AND value IS NOT NULL` is 0 on every compiled artifact). A MASKED BINDING RENDERS `hidden: true` AND SHIPS NO `value` KEY, which is GitLab's own vocabulary rather than a convenience: `masked` means blanked out of JOB LOGS and `hidden` (created as `masked_and_hidden`) means THE API NEVER RETURNS THE VALUE, so `masked: true, hidden: false` with the key dropped would be a shape real GitLab never sends. An unmasked binding's `value` is the artifact row `ci_binding.value_source` names — the collector's endpoint, the default branch, the orb document's tree path — resolved at compile time, so the setting and the thing it configures cannot drift apart. `variable_type` is `env_var` on every row, which agrees with /api/v4/projects/{id}/secure_files being ruled empty: a `file` variable IS a secure file. `raw` and `description` are OMITTED rather than defaulted — neither is required by APIEntitiesCiVariable and neither is a fact canon holds (the depth-hooks.ts rule, one wave earlier). Conformance drives each row against the vendored schema AND against the reader accessor the renderer must have used, walks the pages with the headers asserted, and fails if a masked row ever carries a value. Gated on hasCiBindings(); an older artifact answers the named hello-19 CI-variable gap, because [] there would say this organization configures its pipelines with nothing while the same artifact publishes a CI document, a nightly schedule and deployments to two environments
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
generate — RE-SCOPED 2026-09-09: the 23-row CiVariable+PipelineSchedule+ResourceGroup+SecureFile+FreezePeriod clique was hiding a solved fifth — hello-15's canon.WorkflowSchedule answers the PIPELINE SCHEDULES (see /api/v4/projects/{id}/pipeline_schedules). A JOB TOKEN is not: the scope rows are the cross-project trust list a CI job's token may reach, and the trigger rows are the tokens that start a pipeline from outside. canon stores no token (the PagerDuty routing-key rule, DECISIONS 2026-09-03) and this universe holds one project, so neither the allowlist nor the trigger has anything to point at. New canon entities JobTokenScope and PipelineTrigger
generate — RE-SCOPED 2026-09-09: the 23-row CiVariable+PipelineSchedule+ResourceGroup+SecureFile+FreezePeriod clique was hiding a solved fifth — hello-15's canon.WorkflowSchedule answers the PIPELINE SCHEDULES (see /api/v4/projects/{id}/pipeline_schedules). A JOB TOKEN is not: the scope rows are the cross-project trust list a CI job's token may reach, and the trigger rows are the tokens that start a pipeline from outside. canon stores no token (the PagerDuty routing-key rule, DECISIONS 2026-09-03) and this universe holds one project, so neither the allowlist nor the trigger has anything to point at. New canon entities JobTokenScope and PipelineTrigger
generate — canon has no SecurityFinding/SbomComponent/Attestation; a scanning org has findings
generate — canon has no SecurityFinding/SbomComponent/Attestation; a scanning org has findings
refuse — the export object can only be created by a POST we refuse, so the by-id read can only ever be the vendor's own 404
refuse — the export object can only be created by a POST we refuse, so the by-id read can only ever be the vendor's own 404
refuse — the export object can only be created by a POST we refuse, so the by-id read can only ever be the vendor's own 404
refuse — the export object can only be created by a POST we refuse, so the by-id read can only ever be the vendor's own 404
generate — canon has no SecurityFinding/SbomComponent/Attestation; a scanning org has findings
generate — canon has no SecurityFinding/SbomComponent/Attestation; a scanning org has findings
refuse — certificate-based cluster integration is disabled on GitLab.com and removed upstream; mirroring it would advertise a surface no current client can reach
refuse — certificate-based cluster integration is disabled on GitLab.com and removed upstream; mirroring it would advertise a surface no current client can reach
refuse — certificate-based cluster integration is disabled on GitLab.com and removed upstream; mirroring it would advertise a surface no current client can reach
refuse — certificate-based cluster integration is disabled on GitLab.com and removed upstream; mirroring it would advertise a surface no current client can reach
refuse — certificate-based cluster integration is disabled on GitLab.com and removed upstream; mirroring it would advertise a surface no current client can reach
refuse — certificate-based cluster integration is disabled on GitLab.com and removed upstream; mirroring it would advertise a surface no current client can reach
refuse — certificate-based cluster integration is disabled on GitLab.com and removed upstream; mirroring it would advertise a surface no current client can reach
generate — canon has no Badge; project READMEs in this universe carry pipeline and coverage badges
generate — canon has no Badge; project READMEs in this universe carry pipeline and coverage badges
generate — canon has no Badge; project READMEs in this universe carry pipeline and coverage badges
generate — canon has no Badge; project READMEs in this universe carry pipeline and coverage badges
generate — canon has no Badge; project READMEs in this universe carry pipeline and coverage badges
generate — canon has no Badge; project READMEs in this universe carry pipeline and coverage badges
refuse — no commit or tag in this universe is GPG-signed, and GitLab answers its own 404 'GPG Signature Not Found' for an unsigned object
generate — canon has deployments but no ClusterAgent that performed them
generate — canon has deployments but no ClusterAgent that performed them
generate — canon has deployments but no ClusterAgent that performed them
generate — canon has deployments but no ClusterAgent that performed them
generate — canon has deployments but no ClusterAgent that performed them
generate — canon has deployments but no ClusterAgent that performed them
refuse — GitLab restricts custom attributes to instance administrators; a non-admin token gets 403
refuse — GitLab restricts custom attributes to instance administrators; a non-admin token gets 403
refuse — GitLab restricts custom attributes to instance administrators; a non-admin token gets 403
refuse — GitLab restricts custom attributes to instance administrators; a non-admin token gets 403
refuse — GitLab restricts custom attributes to instance administrators; a non-admin token gets 403
refuse — GitLab restricts custom attributes to instance administrators; a non-admin token gets 403
olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule, so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12
olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule, so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12
refuse — olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. THE NAMED HALF: no name resolves over a collection that is empty, so GitLab's own 404 is the whole answer — `[]` here would assert that the thing exists and holds nothing. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. THE NAMED HALF: no name resolves over a collection that is empty, so GitLab's own 404 is the whole answer — `[]` here would assert that the thing exists and holds nothing. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
generate — canon has no Integration; this universe literally has a Jira and a Slack to be integrated with
generate — canon has no Integration; this universe literally has a Jira and a Slack to be integrated with
generate — canon has no Integration; this universe literally has a Jira and a Slack to be integrated with
generate — canon has no Integration; this universe literally has a Jira and a Slack to be integrated with
generate — canon has no Integration; this universe literally has a Jira and a Slack to be integrated with
generate — canon has no Integration; this universe literally has a Jira and a Slack to be integrated with
refuse — a self-managed license does not exist on a SaaS-shaped mirror and GitLab refuses these to non-admins; managed_licenses is the removed License-Compliance feature
refuse — a self-managed license does not exist on a SaaS-shaped mirror and GitLab refuses these to non-admins; managed_licenses is the removed License-Compliance feature
refuse — a self-managed license does not exist on a SaaS-shaped mirror and GitLab refuses these to non-admins; managed_licenses is the removed License-Compliance feature
refuse — this replica mirrors a SaaS-shaped GitLab and holds no instance subscription of its own; GitLab's own 403 for a non-admin token on the licensing surface is the whole answer. THE OLD REASON MISFILED THE ROW: /api/v4/licenses is not the licence TEMPLATE catalog — its 200 is APIEntitiesGitlabLicense (plan, licensee, add_ons, overage, user_limit, historical_max), the same object /api/v4/license and /api/v4/license/{id} return, and both of those have been armed `refuse` at 403 since class 11 of the 2026-09-01 wave. The template catalog is /api/v4/templates/licenses, which this host has served green off license_catalog since wave W8 and which conformance asserts is untouched by this ruling. So this is class 11's own sentence applied to the plural path, and it takes class 11's status. UNGATED: no generation of this universe holds an instance subscription. RULED by the founder 2026-09-12 (round 20)
refuse — a self-managed license does not exist on a SaaS-shaped mirror and GitLab refuses these to non-admins; managed_licenses is the removed License-Compliance feature
refuse — a self-managed license does not exist on a SaaS-shaped mirror and GitLab refuses these to non-admins; managed_licenses is the removed License-Compliance feature
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
features.namespaces (hello-16) adds protected_ref, a rule over a ref PATTERN at namespace scope - two rows, main and release/*. reader.protectedRefs('namespace', id) derives them; older pins answer the empty list. SERVED 2026-09-09 (hello-16)
the by-name twin of the group protection list, off the same reader.protectedRefs rows and accepting a url-encoded pattern (release%2F*); a pattern nobody holds is still GitLab's 404, and that is the whole answer on every pin older than hello-16. SERVED 2026-09-09 (hello-16)
olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule, so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12
refuse — olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. THE NAMED HALF: no name resolves over a collection that is empty, so GitLab's own 404 is the whole answer — `[]` here would assert that the thing exists and holds nothing. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule, so the 404 is true on every artifact. RULED by the founder 2026-09-12
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
refuse — state describes real infrastructure the sandbox does not have; GitLab 404s a state name nobody wrote, and writes are refused
refuse — state describes real infrastructure the sandbox does not have; GitLab 404s a state name nobody wrote, and writes are refused
refuse — this is per-caller editor storage, not a record of the org; it 404s until the caller WRITES settings, and writes are refused
refuse — this is per-caller editor storage, not a record of the org; it 404s until the caller WRITES settings, and writes are refused
refuse — this is per-caller editor storage, not a record of the org; it 404s until the caller WRITES settings, and writes are refused
refuse — this is per-caller editor storage, not a record of the org; it 404s until the caller WRITES settings, and writes are refused
refuse — this is per-caller editor storage, not a record of the org; it 404s until the caller WRITES settings, and writes are refused
refuse — this is per-caller editor storage, not a record of the org; it 404s until the caller WRITES settings, and writes are refused
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Package/PackageFile; a simulated org publishes packages and container images
generate — canon has no Subscription/BillingPeriod; decision 7 of 2026-08-29 puts billing in scope with sandbox-issued ids
refuse — namespace storage-limit exclusions are a GitLab Inc. internal admin surface
"all releases for PROJECTS IN a specified group" — the same rows each project's own /releases serves. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers `200 []`, because it owns no project (founder ruling F1)
no release in olympus-labs carries an asset link — canon's release names a tag and a description and nothing is attached to it; GitLab answers `[]` at 200 for a release with no links and its own 404 for a link id. THE LIST HALF: the release TAG must resolve first, so a tag nobody cut is still `404 Release Not Found`. Paginated, per the operation's own `page`/`per_page`. UNGATED: `canon.Release` (packages/canon/src/entities.ts:342) has carried the same nine fields in every generation and not one of them is an attachment. Falsifiable: the `expectsEmpty` entry reds the row the day a release grows a link. RULED by the founder 2026-09-12
refuse — no release in olympus-labs carries an asset link — canon's release names a tag and a description and nothing is attached to it; GitLab answers `[]` at 200 for a release with no links and its own 404 for a link id. THE LINK-ID HALF: with no link on any release there is no link id to resolve and GitLab's own 404 is the whole answer. UNGATED, for the reason the list half states. RULED by the founder 2026-09-12
SERVED 2026-09-12 (coverage wave W8): `Gitlab::Analytics::GroupActivityCalculator`'s own fold — what was created in the last 90 days, capped at its own RECENT_COUNT_LIMIT of 1000. THE CLOCK IS THE ARTIFACT'S ANCHOR, never Date.now(): the operation's window is relative to "now", and a replica that read the wall clock would answer differently on every request and differently again on a frozen pin a year from now. `group_path` is a REQUIRED parameter and resolves the container the same way `/api/v4/groups/{id}` resolves an id, so both containers this host lists answer and a path nobody has is GitLab's own 404 rather than a count of nothing. The count is over reader.listIssues(repo) across every repository the container owns; conformance recomputes it from the artifact rather than asserting a constant
SERVED 2026-09-12 (coverage wave W8): `Gitlab::Analytics::GroupActivityCalculator`'s own fold — what was created in the last 90 days, capped at its own RECENT_COUNT_LIMIT of 1000. THE CLOCK IS THE ARTIFACT'S ANCHOR, never Date.now(): the operation's window is relative to "now", and a replica that read the wall clock would answer differently on every request and differently again on a frozen pin a year from now. `group_path` is a REQUIRED parameter and resolves the container the same way `/api/v4/groups/{id}` resolves an id, so both containers this host lists answer and a path nobody has is GitLab's own 404 rather than a count of nothing. The count is over reader.listPulls(repo) across every repository the container owns; conformance recomputes it from the artifact rather than asserting a constant
SERVED 2026-09-12 (coverage wave W8): `Gitlab::Analytics::GroupActivityCalculator`'s own fold — what was created in the last 90 days, capped at its own RECENT_COUNT_LIMIT of 1000. THE CLOCK IS THE ARTIFACT'S ANCHOR, never Date.now(): the operation's window is relative to "now", and a replica that read the wall clock would answer differently on every request and differently again on a frozen pin a year from now. `group_path` is a REQUIRED parameter and resolves the container the same way `/api/v4/groups/{id}` resolves an id, so both containers this host lists answer and a path nobody has is GitLab's own 404 rather than a count of nothing. The one count that needs a JOIN DATE, which is why it alone of the three is generation-gated: `membership.added_epoch` ships with hello-16's access domain, and on an older artifact the row answers the membership generation gap rather than 0 — a zero would say nobody joined this group in the window, and an artifact carrying no membership row says nothing at all about when anybody joined. An invitation nobody accepted is NOT a member, so the pending rows are excluded exactly as `/groups/{id}/members` excludes them, which conformance asserts
SERVED 2026-09-12 (coverage wave W8): DORA's PREDECESSOR on this host, and a different shape — `{value, from, to}` per bucket rather than `{date, value}` — folded over the same reader.listDeployments rows, with `environment` and `from` both required as the vendored operation declares. `value` is a `deviation`: the component types it `string` while real GitLab sends the integer count. The clock is the artifact's anchor, for the reason the DORA rows give
no pipeline in olympus-labs serializes on a resource group — every job in canon runs without a concurrency lock; GitLab answers `[]` at 200 for a project with none. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: no generation of this universe takes a concurrency lock on a job, so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12
refuse — no pipeline in olympus-labs serializes on a resource group — every job in canon runs without a concurrency lock; GitLab answers `[]` at 200 for a project with none. THE NAMED HALF: no name resolves over a collection that is empty, so GitLab's own 404 is the whole answer — `[]` here would assert that the thing exists and holds nothing. UNGATED: no generation of this universe takes a concurrency lock on a job, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no pipeline in olympus-labs serializes on a resource group — every job in canon runs without a concurrency lock; GitLab answers `[]` at 200 for a project with none. THE NAMED HALF: no name resolves over a collection that is empty, so GitLab's own 404 is the whole answer — `[]` here would assert that the thing exists and holds nothing. UNGATED: no generation of this universe takes a concurrency lock on a job, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — no pipeline in olympus-labs serializes on a resource group — every job in canon runs without a concurrency lock; GitLab answers `[]` at 200 for a project with none. THE NAMED HALF: no name resolves over a collection that is empty, so GitLab's own 404 is the whole answer — `[]` here would assert that the thing exists and holds nothing. UNGATED: no generation of this universe takes a concurrency lock on a job, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — the export object can only be created by a POST we refuse, so the by-id read can only ever be the vendor's own 404
refuse — the export object can only be created by a POST we refuse, so the by-id read can only ever be the vendor's own 404
generate — canon has no SecurityFinding/SbomComponent/Attestation; a scanning org has findings
generate — canon has no SecurityFinding/SbomComponent/Attestation; a scanning org has findings
generate — canon has no FeatureFlag; a shipping org gates releases behind flags
generate — canon has no FeatureFlag; a shipping org gates releases behind flags
generate — canon has no FeatureFlag; a shipping org gates releases behind flags
generate — canon has no FeatureFlag; a shipping org gates releases behind flags
refuse — the instance-wide Pages domain list is an administrator surface
refuse — olympus-labs publishes no GitLab Pages site — no project in this universe has a pages deployment or a custom domain; GitLab's own 404 for a project with Pages unused is the whole answer. ALL FOUR ROWS REFUSE, the domain list included: a Pages domain collection is addressable only on a project that HAS Pages, and no project here does — a 200 `[]` there would advertise a Pages site with no custom domain rather than the absence of Pages. The vendored document declares `404 Not Found` on all four operations. UNGATED: canon has no PagesSite in any generation. RULED by the founder 2026-09-12
refuse — olympus-labs publishes no GitLab Pages site — no project in this universe has a pages deployment or a custom domain; GitLab's own 404 for a project with Pages unused is the whole answer. ALL FOUR ROWS REFUSE, the domain list included: a Pages domain collection is addressable only on a project that HAS Pages, and no project here does — a 200 `[]` there would advertise a Pages site with no custom domain rather than the absence of Pages. The vendored document declares `404 Not Found` on all four operations. UNGATED: canon has no PagesSite in any generation. RULED by the founder 2026-09-12
refuse — olympus-labs publishes no GitLab Pages site — no project in this universe has a pages deployment or a custom domain; GitLab's own 404 for a project with Pages unused is the whole answer. ALL FOUR ROWS REFUSE, the domain list included: a Pages domain collection is addressable only on a project that HAS Pages, and no project here does — a 200 `[]` there would advertise a Pages site with no custom domain rather than the absence of Pages. The vendored document declares `404 Not Found` on all four operations. UNGATED: canon has no PagesSite in any generation. RULED by the founder 2026-09-12
features.namespaces (hello-16) puts two cross-cutting labels on the subgroup; reader.labelsForNamespace(id) derives them with is_project_label false. The organization's own list is empty and include_descendant_groups reaches the subgroup's, which is GitLab's own rule; older pins answer the empty list. SERVED 2026-09-09 (hello-16)
the by-name twin of the group label list, off the same reader.labelsForNamespace rows; a name nobody holds is still GitLab's 404 Label Not Found, and that is the whole answer on every pin older than hello-16. SERVED 2026-09-09 (hello-16)
SERVED 2026-09-12 (hello-18): one identity by the handle itself — THE ROW THE WHOLE DOMAIN WAS ASKED FOR, since a uid that resolves to a person instead of to nothing is what this clique has named as its blocker since 2026-09-09. Byte-identical to that person's entry in the list, which conformance compares directly; a handle of the right SHAPE that nobody holds is GitLab's own 404, so the refusal is about the value rather than about the path. Same deviation as its list. The subgroup resolves the family — the document restricts it to no particular group — and answers [] for a list and GitLab's 404 for a named read, because a subgroup federates nobody; the organization above it does. Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)
SERVED 2026-09-12 (hello-18): the directory identities themselves — APIEntitiesIdentityDetail over person.external_uid, a 32-character lowercase hex handle (a UUID with its hyphens removed, which is the width Entra and Okta issue) that is a pure function of the root seed and the person's id. DEVIATION RATHER THAN GREEN, and the evidence is the vendored document contradicting itself: APIEntitiesIdentityDetail types `user_id` and `active` as `string`, while GitLab's own published example for these operations is {"extern_uid": "4", "user_id": 48, "active": true} — an integer and a boolean. Invariant #2 mirrors the provider rather than its documentation, so the real types are served and the two errors are declared spec bugs. `active` is true on every row because canon records no deprovisioning. Conformance asserts every handle matches /^[0-9a-f]{32}$/, that no two people share one, and that every user_id resolves to a person this host serves. The subgroup resolves the family — the document restricts it to no particular group — and answers [] for a list and GitLab's 404 for a named read, because a subgroup federates nobody; the organization above it does. Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)
SERVED 2026-09-12 (hello-18): one SCIM identity by the handle, byte-identical to the SAML identity for the same handle — one directory, one identity, asserted rather than assumed. Same deviation as its list. The subgroup resolves the family — the document restricts it to no particular group — and answers [] for a list and GitLab's 404 for a named read, because a subgroup federates nobody; the organization above it does. Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)
SERVED 2026-09-12 (hello-18): the directory identities themselves — APIEntitiesIdentityDetail over person.external_uid, a 32-character lowercase hex handle (a UUID with its hyphens removed, which is the width Entra and Okta issue) that is a pure function of the root seed and the person's id. DEVIATION RATHER THAN GREEN, and the evidence is the vendored document contradicting itself: APIEntitiesIdentityDetail types `user_id` and `active` as `string`, while GitLab's own published example for these operations is {"extern_uid": "4", "user_id": 48, "active": true} — an integer and a boolean. Invariant #2 mirrors the provider rather than its documentation, so the real types are served and the two errors are declared spec bugs. `active` is true on every row because canon records no deprovisioning. THE SCIM IDENTITIES ARE THE SAME IDENTITIES, and that is what one directory means: canon holds a single identity_provider_config with scim_enabled = 1, so a second, different handle here would assert a second directory nobody configured. Conformance compares the two lists element for element. The subgroup resolves the family — the document restricts it to no particular group — and answers [] for a list and GitLab's 404 for a named read, because a subgroup federates nobody; the organization above it does. Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)
olympus-labs documents its services in the parthenon repository, not in a wiki — no WikiPage exists at project or group scope; GitLab answers `[]` at 200 for the wiki list and its own 404 for a slug that names nothing. THE LIST HALF, served UNPAGINATED because the vendored operation declares only `id` and `with_content` — printing pagination headers on an operation GitLab does not paginate would break invariant #2 in the name of consistency. Both container ids answer it: the document says "for a group" and restricts the operation to no particular one, so the subgroup is not denied a family this host lists it under (invariant #5). UNGATED: canon has no WikiPage in any generation. RULED by the founder 2026-09-12
olympus-labs documents its services in the parthenon repository, not in a wiki — no WikiPage exists at project or group scope; GitLab answers `[]` at 200 for the wiki list and its own 404 for a slug that names nothing. THE LIST HALF, served UNPAGINATED because the vendored operation declares only `id` and `with_content` — printing pagination headers on an operation GitLab does not paginate would break invariant #2 in the name of consistency. Both container ids answer it: the document says "for a group" and restricts the operation to no particular one, so the subgroup is not denied a family this host lists it under (invariant #5). UNGATED: canon has no WikiPage in any generation. RULED by the founder 2026-09-12
refuse — olympus-labs documents its services in the parthenon repository, not in a wiki — no WikiPage exists at project or group scope; GitLab answers `[]` at 200 for the wiki list and its own 404 for a slug that names nothing. THE SLUG/PAGE-ID HALF: no slug and no wiki_page_meta_id resolves, so GitLab's own 404 is the whole answer — a 200 `[]` on a wiki page's note list would assert that the page exists and nobody commented on it. UNGATED: canon has no WikiPage in any generation. RULED by the founder 2026-09-12
refuse — olympus-labs documents its services in the parthenon repository, not in a wiki — no WikiPage exists at project or group scope; GitLab answers `[]` at 200 for the wiki list and its own 404 for a slug that names nothing. THE SLUG/PAGE-ID HALF: no slug and no wiki_page_meta_id resolves, so GitLab's own 404 is the whole answer — a 200 `[]` on a wiki page's note list would assert that the page exists and nobody commented on it. UNGATED: canon has no WikiPage in any generation. RULED by the founder 2026-09-12
refuse — the /api/v4/internal/* family is GitLab-to-Gitaly/agent plumbing outside the public API contract; no client library calls it
derive
derive
refuse — application registration is a control-plane surface of the vendor's auth system; SandboxAPIs issues its own keys and the instance list is admin-only
refuse — application registration is a control-plane surface of the vendor's auth system; SandboxAPIs issues its own keys and the instance list is admin-only
refuse — application registration is a control-plane surface of the vendor's auth system; SandboxAPIs issues its own keys and the instance list is admin-only
generate — A WIDENING, NOT A BUILD: canon.AdminAuditEvent exists (packages/canon/src/support-desk.ts:690) and its own doc comment calls it "GENERIC by construction". The action vocabulary (AuditAction, :602), the change vocabulary (AuditChangeSlot, :633), the target vocabulary (AuditTargetKind, :611) and the RFC 5737 source-address rule (AuditSourceBlock, :652) all shipped, and Zendesk serves the table today (reader.listAuditEvents(), ruled 2026-09-07). TWO things are missing and neither is the entity: git-host members on AuditTargetKind, and a generation that WRITES personActor — the field is declared for exactly this wave and "this generation writes none" (:695-697)
generate — A WIDENING, NOT A BUILD: canon.AdminAuditEvent exists (packages/canon/src/support-desk.ts:690) and its own doc comment calls it "GENERIC by construction". The action vocabulary (AuditAction, :602), the change vocabulary (AuditChangeSlot, :633), the target vocabulary (AuditTargetKind, :611) and the RFC 5737 source-address rule (AuditSourceBlock, :652) all shipped, and Zendesk serves the table today (reader.listAuditEvents(), ruled 2026-09-07). TWO things are missing and neither is the entity: git-host members on AuditTargetKind, and a generation that WRITES personActor — the field is declared for exactly this wave and "this generation writes none" (:695-697)
generate — A WIDENING, NOT A BUILD: canon.AdminAuditEvent exists (packages/canon/src/support-desk.ts:690) and its own doc comment calls it "GENERIC by construction". The action vocabulary (AuditAction, :602), the change vocabulary (AuditChangeSlot, :633), the target vocabulary (AuditTargetKind, :611) and the RFC 5737 source-address rule (AuditSourceBlock, :652) all shipped, and Zendesk serves the table today (reader.listAuditEvents(), ruled 2026-09-07). TWO things are missing and neither is the entity: git-host members on AuditTargetKind, and a generation that WRITES personActor — the field is declared for exactly this wave and "this generation writes none" (:695-697)
THE OLD REASON WAS WRONG (DECISIONS 2026-09-06 [HELLO-15/avatars-reactions] finding a): the vendored spec's own summary is "Return avatar url for a user" and its one required parameter is email. It is a PERSON lookup, not an Attachment - PersonAvatar (hello-13) plus the org-wide login@{org}.dev convention every host joins on answer it. An address naming nobody gets avatar_url null, which is real GitLab with Gravatar disabled; a @users.noreply address deliberately does not resolve, because real GitLab looks up the PUBLIC email
the group's own identicon bytes from reader.orgAvatar("org", org) — one picture per container across every host that serves one. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers `404 Avatar Not Found`, GitLab's own answer for a group nobody uploaded an avatar for: `org_avatar` is keyed `org` and `repo` and no row names a namespace, which is also why glSubgroup prints no avatar_url for it. Older pins still answer the noOrgAvatars generation gap
OrgAvatar is canon as of hello-15 (features.orgAvatars): one identicon per repository, subjectKind "repo". orgAvatar("repo", ...) answers it and shapeProject's new avatar_url names the /uploads/-/system/project/avatar/{id}/avatar.png path that serves the same bytes
SERVED 2026-09-12 (hello-18): the credentials of this group's ENTERPRISE USERS, which the vendored document scopes the operation to in as many words. The set is the same sixteen people /enterprise_users answers and the rows are the person_credential rows they already hold — the same rows /users/{id}/keys and /personal_access_tokens serve, re-scoped to the directory's people rather than re-derived, and conformance asserts every row is also on its owner's own list so the inventory and the person cannot disagree. THE MACHINE ACCOUNT'S CREDENTIALS ARE NOT HERE, which is what the exclusion is for: an inventory scoped to enterprise users must not list a principal the enterprise-user surface says does not exist. The vendored operation declares `200: OK` and NO response schema at all — a real GitLab spec gap — so the shape is glPersonalAccessToken, the same object /personal_access_tokens serves, and conformance asserts every id, name, owner and active flag against the credential row rather than against a schema that does not exist. The subgroup gets GitLab's own not_found!, on the vendored description's own top-level scoping. Gated on hasExternalUid() (the set) and hasCredentials() (the rows); an older artifact answers the named generation gap rather than []
SERVED 2026-09-12 (hello-18): the credentials of this group's ENTERPRISE USERS, which the vendored document scopes the operation to in as many words. The set is the same sixteen people /enterprise_users answers and the rows are the person_credential rows they already hold — the same rows /users/{id}/keys and /personal_access_tokens serve, re-scoped to the directory's people rather than re-derived, and conformance asserts every row is also on its owner's own list so the inventory and the person cannot disagree. THE MACHINE ACCOUNT'S CREDENTIALS ARE NOT HERE, which is what the exclusion is for: an inventory scoped to enterprise users must not list a principal the enterprise-user surface says does not exist. AUTHENTICATION KEYS ONLY: the operation's own words are "all SSH public keys associated with enterprise users", and canon's `ssh-signing` rows are excluded on the ruling /users/{selected_user}/ssh-keys already carries one host over — a key authorised to push and a key trusted to attest authorship are different grants, and an inventory is the first place conflating them would matter. APIEntitiesSshKeyWithUserId declares seven properties and the key MATERIAL is not one of them, so it is absent rather than repeated: this route answers WHO HOLDS WHAT and /users/{id}/keys is where the bytes are. The subgroup gets GitLab's own not_found!, on the vendored description's own top-level scoping. Gated on hasExternalUid() (the set) and hasCredentials() (the rows); an older artifact answers the named generation gap rather than []
SERVED 2026-09-10 (hello-16): "all group and project access tokens associated with a TOP-LEVEL-GROUP" — the group token plus every project token below it, from the same two reader.listCredentials calls, and conformance asserts the managed list is exactly the UNION of the narrower ones so a token cannot appear here without appearing on its own container. THE SUBGROUP GETS GITLAB'S 404 rather than a list: the document restricts the operation to a top-level group, and an empty list would report a different fact from the one the provider reports. Gated on hasCredentials()
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
olympus-labs defines no custom member role — the five roles in canon's membership_role table are GitLab's own built-ins; GitLab answers `[]` at 200 for a group that has minted none. THE LIST HALF, served UNPAGINATED because the vendored operation declares `id` and nothing else. Both container ids answer it: the document restricts the operation to no particular group, so the subgroup is not denied a family this host lists it under (invariant #5). UNGATED: no generation of this universe mints a custom role — canon's five roles are GitLab's own built-ins. RULED by the founder 2026-09-12
refuse — instance-level custom roles are a self-managed admin surface; GitLab.com answers 404 for them
refuse — instance-level custom roles are a self-managed admin surface; GitLab.com answers 404 for them
olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule, so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12
refuse — olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. THE NAMED HALF: no name resolves over a collection that is empty, so GitLab's own 404 is the whole answer — `[]` here would assert that the thing exists and holds nothing. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule, so the 404 is true on every artifact. RULED by the founder 2026-09-12
refuse — olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. THE NAMED HALF: no name resolves over a collection that is empty, so GitLab's own 404 is the whole answer — `[]` here would assert that the thing exists and holds nothing. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule, so the 404 is true on every artifact. RULED by the founder 2026-09-12
generate — canon has no NotificationSetting for a persona
generate — canon has no NotificationSetting for a persona
generate — canon has no NotificationSetting for a persona
generate — RE-SCOPED 2026-09-09: the 23-row CiVariable+PipelineSchedule+ResourceGroup+SecureFile+FreezePeriod clique was hiding a solved fifth — hello-15's canon.WorkflowSchedule answers the PIPELINE SCHEDULES (see /api/v4/projects/{id}/pipeline_schedules). A JOB TOKEN is not: the scope rows are the cross-project trust list a CI job's token may reach, and the trigger rows are the tokens that start a pipeline from outside. canon stores no token (the PagerDuty routing-key rule, DECISIONS 2026-09-03) and this universe holds one project, so neither the allowlist nor the trigger has anything to point at. New canon entities JobTokenScope and PipelineTrigger
generate — RE-SCOPED 2026-09-09: the 23-row CiVariable+PipelineSchedule+ResourceGroup+SecureFile+FreezePeriod clique was hiding a solved fifth — hello-15's canon.WorkflowSchedule answers the PIPELINE SCHEDULES (see /api/v4/projects/{id}/pipeline_schedules). A JOB TOKEN is not: the scope rows are the cross-project trust list a CI job's token may reach, and the trigger rows are the tokens that start a pipeline from outside. canon stores no token (the PagerDuty routing-key rule, DECISIONS 2026-09-03) and this universe holds one project, so neither the allowlist nor the trigger has anything to point at. New canon entities JobTokenScope and PipelineTrigger
generate — RE-SCOPED 2026-09-09: the 23-row CiVariable+PipelineSchedule+ResourceGroup+SecureFile+FreezePeriod clique was hiding a solved fifth — hello-15's canon.WorkflowSchedule answers the PIPELINE SCHEDULES (see /api/v4/projects/{id}/pipeline_schedules). A JOB TOKEN is not: the scope rows are the cross-project trust list a CI job's token may reach, and the trigger rows are the tokens that start a pipeline from outside. canon stores no token (the PagerDuty routing-key rule, DECISIONS 2026-09-03) and this universe holds one project, so neither the allowlist nor the trigger has anything to point at. New canon entities JobTokenScope and PipelineTrigger
nothing here mirrors to or from another host: a remote mirror exists only because someone configured one, and this universe accepts no writes
refuse — the collection this id would index is empty in this universe, so the only honest answer is the vendor's own 404
refuse — the collection this id would index is empty in this universe, so the only honest answer is the vendor's own 404
olympus-labs stores no CI secure file — canon holds no secret VALUE and no uploaded secret FILE; GitLab answers `[]` at 200 for a project with none. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: no generation of this universe stores a secret VALUE or an uploaded secret FILE — hello-19's `ci_binding` stores secret NAMES and dates and has no column that could hold a value, which is asserted as SQL rather than reviewed — so every registered pin answers this same `[]`, and so does every generation after it. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12; the clause was narrowed on 2026-09-12 in hello-19 wave W-b (founder call F4) from the broader claim that canon holds no secret material of any kind — same conclusion, same mode, same review date, and a narrower fact that stays permanently checkable now that a generation stores secret NAMES
refuse — olympus-labs stores no CI secure file — canon holds no secret VALUE and no uploaded secret FILE; GitLab answers `[]` at 200 for a project with none. THE NAMED HALF: no name resolves over a collection that is empty, so GitLab's own 404 is the whole answer — `[]` here would assert that the thing exists and holds nothing. UNGATED: no generation of this universe stores a secret VALUE or an uploaded secret FILE — hello-19's `ci_binding` stores secret NAMES and dates and has no column that could hold a value, which is asserted as SQL rather than reviewed — so the 404 is true on every artifact. RULED by the founder 2026-09-12; the clause was narrowed on 2026-09-12 in hello-19 wave W-b (founder call F4) from the broader claim that canon holds no secret material of any kind — same conclusion, same mode, same review date, and a narrower fact that stays permanently checkable now that a generation stores secret NAMES
refuse — olympus-labs stores no CI secure file — canon holds no secret VALUE and no uploaded secret FILE; GitLab answers `[]` at 200 for a project with none. THE NAMED HALF: no name resolves over a collection that is empty, so GitLab's own 404 is the whole answer — `[]` here would assert that the thing exists and holds nothing. UNGATED: no generation of this universe stores a secret VALUE or an uploaded secret FILE — hello-19's `ci_binding` stores secret NAMES and dates and has no column that could hold a value, which is asserted as SQL rather than reviewed — so the 404 is true on every artifact. RULED by the founder 2026-09-12; the clause was narrowed on 2026-09-12 in hello-19 wave W-b (founder call F4) from the broader claim that canon holds no secret material of any kind — same conclusion, same mode, same review date, and a narrower fact that stays permanently checkable now that a generation stores secret NAMES
every actor in olympus-labs is a person on the roster — canon mints no bot principal, and a service account served here would be a member no other host lists (invariant #5); GitLab answers `[]` at 200 for a group with none. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: canon has no `ServiceAccount` in any generation — hello-17 dropped the federation domain that would have introduced one, so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12
every actor in olympus-labs is a person on the roster — canon mints no bot principal, and a service account served here would be a member no other host lists (invariant #5); GitLab answers `[]` at 200 for a group with none. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: canon has no `ServiceAccount` in any generation — hello-17 dropped the federation domain that would have introduced one, so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12
every actor in olympus-labs is a person on the roster — canon mints no bot principal, and a service account served here would be a member no other host lists (invariant #5); GitLab answers `[]` at 200 for a group with none. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: canon has no `ServiceAccount` in any generation — hello-17 dropped the federation domain that would have introduced one, so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12
generate — canon has no FeatureFlag; a shipping org gates releases behind flags
generate — canon has no FeatureFlag; a shipping org gates releases behind flags
generate — canon has no FeatureFlag; a shipping org gates releases behind flags
refuse — service ping reports what a real installation sends to GitLab Inc.; a mirror has no installation to report on and the endpoints are admin-only
refuse — service ping reports what a real installation sends to GitLab Inc.; a mirror has no installation to report on and the endpoints are admin-only
refuse — service ping reports what a real installation sends to GitLab Inc.; a mirror has no installation to report on and the endpoints are admin-only
membership here is granted directly by the group's own scaffolding; nobody has ever REQUESTED access, so the pending-request list is truthfully empty. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers the SAME empty list: an access request is an ACTION this canon records none of, which is a fact about the universe rather than about one container
the project-scoped twin of the group access-request list, held to the same fact: membership is granted directly, so nothing is pending
generate — canon has no SecurityFinding/SbomComponent/Attestation; a scanning org has findings
generate — canon has no SecurityFinding/SbomComponent/Attestation; a scanning org has findings
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
SERVED 2026-09-12 (coverage wave W8): the four DORA metrics folded over reader.listDeployments, listIncidents and pullsForCommit — no new fact, and a caller can recompute every value from `/projects/{id}/deployments` and `/projects/{id}/merge_requests`, which is exactly what conformance does. deployment_frequency counts the successful deployments in the requested environment tiers; lead_time_for_changes is the median of (deployment instant − merge instant) over the merge requests each deployment shipped, reached through the same pullsForCommit read `/deployments/{id}/merge_requests` serves so a lead time and that list cannot disagree; time_to_restore_service is the median of (resolved − opened) over the incidents whose canonical issue belongs to the scope; change_failure_rate is those incidents over those deployments. A MEDIAN OF NOTHING IS NULL, never a zero — a lead time of no time is a different claim from no measurement — while a COUNT of nothing is 0, and conformance drives a window with no deployment to hold that line. THE SHAPE IS PINNED HERE because the vendored document declares NO schema for the 200 at all (`"200": {"description": "successful operation"}` and nothing else): an entry per bucket, `{date, value}`, `date` being the bucket's first UTC day. THE CLOCK IS THE ANCHOR: `end_date` defaults to the artifact's own anchor day and `start_date` to 90 UTC days before it, the same window the group-activity counts use. The group folds every repository it owns. This universe has one, so the group series and the project series must AGREE — conformance compares them rather than leaving the coincidence implicit
SERVED 2026-09-12 (coverage wave W8): the four DORA metrics folded over reader.listDeployments, listIncidents and pullsForCommit — no new fact, and a caller can recompute every value from `/projects/{id}/deployments` and `/projects/{id}/merge_requests`, which is exactly what conformance does. deployment_frequency counts the successful deployments in the requested environment tiers; lead_time_for_changes is the median of (deployment instant − merge instant) over the merge requests each deployment shipped, reached through the same pullsForCommit read `/deployments/{id}/merge_requests` serves so a lead time and that list cannot disagree; time_to_restore_service is the median of (resolved − opened) over the incidents whose canonical issue belongs to the scope; change_failure_rate is those incidents over those deployments. A MEDIAN OF NOTHING IS NULL, never a zero — a lead time of no time is a different claim from no measurement — while a COUNT of nothing is 0, and conformance drives a window with no deployment to hold that line. THE SHAPE IS PINNED HERE because the vendored document declares NO schema for the 200 at all (`"200": {"description": "successful operation"}` and nothing else): an entry per bucket, `{date, value}`, `date` being the bucket's first UTC day. THE CLOCK IS THE ANCHOR: `end_date` defaults to the artifact's own anchor day and `start_date` to 90 UTC days before it, the same window the group-activity counts use. `metric` is required and its vocabulary is closed; an unknown one is GitLab's own 400 rather than a silently different answer
SERVED 2026-09-12 (hello-18): THE SAME SIXTEEN PEOPLE ANSWER saml_users, provisioned_users AND enterprise_users, and that is GitLab's own definition rather than a shortcut: an enterprise user is one the group provisioned through its own SAML/SCIM, and a SAML user and a provisioned user are that sentence read from the other two ends. canon carries ONE identity_provider_config (protocol saml, scim_enabled 1) and one external_uid per person, so the three lists are one set and conformance asserts they are identical rather than leaving it a coincidence. THE MACHINE ACCOUNT IS EXCLUDED and that is the domain's load-bearing ruling (decisions/2026-09-12-1120): canon writes no external_uid for the CI bot, because a directory provisions humans and GitLab's word for a machine principal is a SERVICE ACCOUNT — a different object, a different API family, and one five armed `…/service_accounts` rulings say this universe does not mint. The rows are glUser, byte-identical to what /api/v4/users/{id} serves for each of them, which conformance checks person by person. THE SUBGROUP GETS GITLAB'S OWN not_found!, on the vendored description's own words ("for a specified top-level group"). Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)
SERVED 2026-09-12 (hello-18): one enterprise user by numeric id, byte-identical to that person's entry in the list. A PERSON THE ROSTER HAS AND THE DIRECTORY DID NOT PROVISION IS GITLAB'S 404 HERE, not a 200, and that is the whole point of the endpoint: it answers who is an enterprise user rather than who exists. Conformance drives the CI bot's own id, asserts the 404, and asserts the same id DOES resolve at /api/v4/users/{id} — so the refusal is about the family rather than about an id nobody has. The subgroup gets GitLab's own not_found! with its list. Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)
generate — canon has no Integration; this universe literally has a Jira and a Slack to be integrated with
generate — canon has no Integration; this universe literally has a Jira and a Slack to be integrated with
refuse — these report GitLab Inc.'s own A/B assignments for the caller, are admin-only, and describe the vendor rather than the org
refuse — these report GitLab Inc.'s own A/B assignments for the caller, are admin-only, and describe the vendor rather than the org
olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule, so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12
olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. THE LIST HALF, and the merge request RESOLVES — so the empty page is a statement about that merge request rather than about an iid nobody has. Served UNPAGINATED because the vendored operation declares only its two path parameters; printing pagination headers on an operation GitLab does not paginate would break invariant #2 in the name of consistency. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule. RULED by the founder 2026-09-12
olympus-labs declares no deploy freeze — canon carries no freeze window; GitLab answers `[]` at 200 for a project with none. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: no generation of this universe opens a freeze window, so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12
refuse — olympus-labs declares no deploy freeze — canon carries no freeze window; GitLab answers `[]` at 200 for a project with none. THE NAMED HALF: no name resolves over a collection that is empty, so GitLab's own 404 is the whole answer — `[]` here would assert that the thing exists and holds nothing. UNGATED: no generation of this universe opens a freeze window, so the 404 is true on every artifact. RULED by the founder 2026-09-12
SERVED 2026-09-12 (coverage wave W8): a GitLab iteration IS canon's sprint — reader.listSprints() — which is the same object Jira serves as a Sprint and Azure DevOps as an iteration path, so the 27 windows and their dates are one set read three ways (invariant #5). THE iid IS GROUP-WIDE AND `sequence` IS PER-CADENCE, and that is the one decision this mapping makes: `sprint.idx` counts within a TEAM and four teams each run their own series, so `idx` repeats four times over and cannot be an iid GitLab guarantees unique inside a group — an iteration cadence being exactly one team's series, `sequence` IS `sprint.idx` and the iid is the group-wide rank by start date, ties broken by team and then by index so the numbering is total and stable. `state` is GitLab's INTEGER enum (started 2, closed 3); nothing here is ever upcoming, because the generator closes a sprint whose end falls before the window's end and starts none after it. `title` and `description` are OMITTED, and that is GitLab's own shape rather than a gap: iteration titles were removed in GitLab 15.6 and the field has been nullable since. `created_at`/`updated_at` are omitted because canon's sprint carries no instant beyond its start and end. `state=` is a real narrowing; `upcoming` truthfully matches nothing, the posture `pipelines?status=` already takes for an enum member this generator never produces
SERVED 2026-09-12 (coverage wave W8): a GitLab iteration IS canon's sprint — reader.listSprints() — which is the same object Jira serves as a Sprint and Azure DevOps as an iteration path, so the 27 windows and their dates are one set read three ways (invariant #5). THE iid IS GROUP-WIDE AND `sequence` IS PER-CADENCE, and that is the one decision this mapping makes: `sprint.idx` counts within a TEAM and four teams each run their own series, so `idx` repeats four times over and cannot be an iid GitLab guarantees unique inside a group — an iteration cadence being exactly one team's series, `sequence` IS `sprint.idx` and the iid is the group-wide rank by start date, ties broken by team and then by index so the numbering is total and stable. `state` is GitLab's INTEGER enum (started 2, closed 3); nothing here is ever upcoming, because the generator closes a sprint whose end falls before the window's end and starts none after it. `title` and `description` are OMITTED, and that is GitLab's own shape rather than a gap: iteration titles were removed in GitLab 15.6 and the field has been nullable since. `created_at`/`updated_at` are omitted because canon's sprint carries no instant beyond its start and end. BYTE-IDENTICAL to the group's list, which conformance compares directly: an iteration belongs to the group, and `include_ancestors` — GitLab's own default — is why a project sees them at all. Two different lists would be this host disagreeing with itself about one cadence
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — project aliases exist only to redirect an in-flight GitHub-import mirror and are admin-only
refuse — project aliases exist only to redirect an in-flight GitHub-import mirror and are admin-only
refuse — these return a shell script that provisions the CALLER's real Google Cloud project; nothing in the response describes the simulated org
refuse — these return a shell script that provisions the CALLER's real Google Cloud project; nothing in the response describes the simulated org
SERVED 2026-09-12 (coverage wave W8) (hello-16 `refCatalogs`): reader.listLicenses()/listGitignoreTemplates() over `license_catalog` (13 rows, bodies included) and `gitignore_template` (163 rows, source included) — the SAME two compile-time tables GitHub's /licenses and /gitignore/templates already serve, which is what makes a licence fetched through either host the same licence (invariant #5). GENERATION-GATED and `[]` is not an option: an artifact without the catalogs answers the template-catalog generation gap, because `[]` here would say GitLab bundles no licence templates — a claim about the HOST rather than about this organization, and a false one. licence: FACTUAL/CC0 — the licence texts are the licences themselves and the ignore-file templates are github/gitignore's CC0-1.0 corpus, the same grant coverage/reviews/2026-09-09-hello-15-stale-reasons-git-hosts.md records for the GitHub twins. The project-scoped read is the SAME catalog, and conformance asserts the two lists are identical rather than merely similar. TWO OF THE SIX DECLARED TYPES ANSWER: `licenses` and `gitignores`, the two canon carries a catalog for. `dockerfiles`, `gitlab_ci_ymls`, `issues` and `merge_requests` answer the COVERAGE 404 naming the manifest rather than `[]` — an empty list would be a claim about GitLab's bundled set, or about this project's absent `.gitlab/issue_templates` directory, that nothing here checked (invariant #4). Conformance drives all four and asserts the 404
SERVED 2026-09-12 (coverage wave W8) (hello-16 `refCatalogs`): reader.license(key)/gitignoreTemplate(name) over `license_catalog` (13 rows, bodies included) and `gitignore_template` (163 rows, source included) — the SAME two compile-time tables GitHub's /licenses and /gitignore/templates already serve, which is what makes a licence fetched through either host the same licence (invariant #5). GENERATION-GATED and `[]` is not an option: an artifact without the catalogs answers the template-catalog generation gap, because `[]` here would say GitLab bundles no licence templates — a claim about the HOST rather than about this organization, and a false one. licence: FACTUAL/CC0 — the licence texts are the licences themselves and the ignore-file templates are github/gitignore's CC0-1.0 corpus, the same grant coverage/reviews/2026-09-09-hello-15-stale-reasons-git-hosts.md records for the GitHub twins. BYTE-IDENTICAL to the instance-scoped read of the same template, which conformance compares directly: one catalog, two addresses. The vendored document types this operation's 200 as `APIEntitiesLicense` for EVERY type, which is true of one of the six — real GitLab presents `Entities::Template` for an ignore file, and invariant #2 mirrors the provider rather than its documentation. The same four types that are not covered on the collection are not covered here
SERVED 2026-09-12 (coverage wave W3): the distinct topics across this instance's projects with the number of projects carrying each, read from repo.topics_json. THE OLD REASON - canon's repo carries no topics - WAS FALSE: the column is real, shapeProject has published it as `topics`/`tag_list` on every project object since wave A, and ?topic= already filters the project list by it. Sorted by project count descending, which is GitLab's own order, tie-broken by name so one artifact always paginates one way. `title`, `description`, `avatar_url` and `organization_id` are omitted: a GitLab topic is an object an administrator creates and titles, canon carries the SLUG and nothing else, and title-casing the slug would be a string this universe never wrote. `search` narrows and is asserted to; `without_projects=true` is empty because every topic here IS a project's own tag; `organization_id` is an explicit error because this universe models no GitLab organization
SERVED 2026-09-12 (coverage wave W3): the by-id twin of /api/v4/topics, addressed by the id minted from the topic's own name - two projects tagged `platform` are tagged the same topic. Conformance asserts the list and the by-id row render one topic the same way and that an unknown id is GitLab's 404. Same falsified reason as the list
refuse — olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. NOT A NAMED HALF AND NOT A COLLECTION: `push_rule` is a SINGULAR sub-resource that takes no name, so `[]` is not a shape it can take at all — there is simply no rule object to render, and the vendored operation declares exactly two responses, a 200 with the rule and a 404 without one. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule. RULED by the founder 2026-09-12
refuse — olympus-labs protects only branches — canon's protected_ref carries two branch patterns at namespace scope and no environment, tag, merge train, push rule or external status check exists; GitLab answers `[]` at 200 for each of these lists and its own 404 for a named one. NOT A NAMED HALF AND NOT A COLLECTION: `push_rule` is a SINGULAR sub-resource that takes no name, so `[]` is not a shape it can take at all — there is simply no rule object to render, and the vendored operation declares exactly two responses, a 200 with the rule and a 404 without one. UNGATED: no generation of this universe has ever protected an environment or a tag, queued a merge train, registered an external status check or set a push rule. RULED by the founder 2026-09-12
refuse — runner managers, controllers, tokens and router discovery describe physical CI machines and their credentials, not the org; GitLab restricts them to admins
refuse — runner managers, controllers, tokens and router discovery describe physical CI machines and their credentials, not the org; GitLab restricts them to admins
SERVED 2026-09-12 (hello-17): the groups this organization's directory asserts and what each may do here — APIEntitiesSamlGroupLink over canon's identity_group (the asserted group, hello-15) joined to its one membership row at scope_kind = 'group' (hello-17). `access_level` maps canon's five role keys onto GitLab's own five constants by what the role's permissions column actually grants: owner 50, admin 40, maintainer 40, member 30, reader 10. `provider` is the directory's own protocol rather than a literal, so a universe that ever ran OIDC would say so. `member_role_id` IS OMITTED and that is a statement: it names a CUSTOM member role, olympus-labs mints none — /groups/{id}/member_roles is armed `empty` on exactly that fact — and an id here would address a role this host says does not exist. A group with no role is not linked at all rather than linked at an invented level. GATED ON THE DIRECTORY AND ON hasGrants(), NOT ON hasExternalUid(), and that is the wave's one deliberate asymmetry: a SAML group link names a GROUP, not a person, so it needs identity_group (hello-15) and the group's own role (hello-17 features.grants, reader.groupRole) and no per-person handle at all. Both halves are already on the LIVE artifact, so these two rows answer TODAY rather than waiting for hello-18's cutover — gating them on a uid they do not read would have reported a gap for a fact that is present. The subgroup answers [] for the list and GitLab's 404 for a named link: the directory hangs off the organization
SERVED 2026-09-12 (hello-17): one link by the directory group's name, byte-identical to its entry in the list. `?provider=` IS HONOURED RATHER THAN IGNORED: naming this directory's own protocol narrows to the same link and naming one nobody runs is GitLab's 404, which conformance drives both ways — a silently-ignored filter that still answered 200 is the wrong answer this renderer refuses to give. A name the directory does not assert is GitLab's own 404. GATED ON THE DIRECTORY AND ON hasGrants(), NOT ON hasExternalUid(), and that is the wave's one deliberate asymmetry: a SAML group link names a GROUP, not a person, so it needs identity_group (hello-15) and the group's own role (hello-17 features.grants, reader.groupRole) and no per-person handle at all. Both halves are already on the LIVE artifact, so these two rows answer TODAY rather than waiting for hello-18's cutover — gating them on a uid they do not read would have reported a gap for a fact that is present. The subgroup answers [] for the list and GitLab's 404 for a named link: the directory hangs off the organization
refuse — the /api/v4/internal/* family is GitLab-to-Gitaly/agent plumbing outside the public API contract; no client library calls it
refuse — the /api/v4/internal/* family is GitLab-to-Gitaly/agent plumbing outside the public API contract; no client library calls it
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
generate — canon has no Subscription/BillingPeriod; decision 7 of 2026-08-29 puts billing in scope with sandbox-issued ids
refuse — these answer a running runner presenting a CI job token, not an API caller; a frozen universe has no live job
SERVED 2026-09-12 (hello-18): APIEntitiesMetricImage over the picture the responder took of each alert the incident opened — one screenshot-svg per alert, dated to the moment the series fell back under the threshold, because a picture of a breach is taken once the breach has a shape. WHICH PROJECT AN ALERT BELONGS TO IS DERIVED rather than assumed from there being one: an alert names the incident it opened, an incident names the issue the git canon filed, and that issue names its project — so an alert that opened no incident belongs to no project and is addressable nowhere, which is the truthful answer rather than a default. `alert_iid` is its 1-based position in that project's own alert list, in listAlerts() id order: mint order, therefore never renumbered, the same rule the merge-request and issue iid sequences follow. `file_path` is GitLab's own system-store template with this file's id and name in it — the vendored component's own example path — so it is reproducible rather than decorative; `url` and `url_text` are OMITTED because they are the dashboard link a responder typed beside the image and canon records none. Conformance asserts every image is an image by its content type, that none predates the alert it pictures, and that an alert iid nobody has is GitLab's own 404. Gated on hasAttachmentParents()
the parthenon repository has no `.gitlab-ci.yml` — its pipelines are defined in `.circleci/` (ci_config_document, 3 rows, all under .circleci/) and `.github/workflows/`, and no tree entry names a GitLab CI file; GitLab's own answer for a project with no config is a 200 carrying `valid: false` and its own "Please provide content of .gitlab-ci.yml" error. NOT A COLLECTION: the operation returns `APIEntitiesCiLintResult`, an object, so the honest empty is GitLab's own failed lint rather than `[]`. `merged_yaml`, `includes` and `jobs` are OMITTED rather than nulled — there is no configuration to merge, nothing to include and no job to describe, all three are optional in the vendored component, and a null typed `string` would buy a deviation for nothing. UNGATED: no generation of this repository's tree carries a `.gitlab-ci.yml`. Falsifiable: the `expectsEmpty` conformance entry (`ciLintEmptyErrors`) names every field and reds the row the day one appears. RULED by the founder 2026-09-12
SERVED 2026-09-12 (coverage wave W8): GitLab's own definition of the review queue, in its own words — the OPEN merge requests that have at least one non-author comment, ordered by review time with the longest first. reader.listPulls + pullReviewComments + listReviews; a merge request nobody has reviewed is absent rather than listed with a null time, which is what makes the queue a queue. `review_time` is whole hours from the first non-author note to the NEWEST INSTANT this universe records against that merge request (`mrUpdatedEpoch`, the same value `updated_at` publishes, widened by the conversation's own last note and last review): a snapshot has a last recorded event rather than a wall clock, canon carries review comments that postdate the anchor, and anchoring the subtraction would print a negative duration for a merge request that is plainly under review. `approved_by` is the approving reviews' authors and is a `deviation`, the vendored component typing a collection as one object. `diff_stats` is OMITTED: canon's `pull` carries no line counts at all and inventing them would be a placeholder
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
generate — canon has no Integration; this universe literally has a Jira and a Slack to be integrated with
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — /api/v4/mcp is a JSON-RPC/SSE agent channel, not a read of org data; SandboxAPIs serves its own MCP server and two tool surfaces would conflict
RE-RULED 2026-09-12 by the founder, replacing the `generate` this row carried since the hello-18 upload wave. THE FACTS DID NOT MOVE, THE VERDICT DID. GitLab scopes this operation to INCIDENTS and this universe HAS incident issues, so the collection is addressable and real; what it holds is nothing. The responder's picture IS in this universe - it is attached to the ALERT, and /projects/{id}/alert_management_alerts/{alert_iid}/metric_images has published it since hello-18 - so rendering the same canonical object under the issue as well would publish one upload twice (invariant #5). The one file canon hangs off the incident issue is a har-summary (requests.har), a capture rather than a picture of a metric. `generate` said the opposite - that the row was waiting on canon - and a row waiting forever for a file nobody would attach is a backlog entry rather than a plan. NO GENERATION GATE, unlike the alert half: `[]` is true on every artifact this fleet has ever published, because no generation has hung an image attachment off the incident issue. Both parents still resolve. RULED by the founder 2026-09-12, reason as given in decisions/2026-09-12-2026-post-h18-serve-pass.md.
refuse — service ping reports what a real installation sends to GitLab Inc.; a mirror has no installation to report on and the endpoints are admin-only
refuse — GitLab answers 403 to a non-admin token here (Geo, Sidekiq, storage moves, LDAP, application settings, feature toggles, background migrations); a tenant-shaped mirror has no instance to administer
refuse — the /api/v4/internal/* family is GitLab-to-Gitaly/agent plumbing outside the public API contract; no client library calls it
refuse — the collection this id would index is empty in this universe, so the only honest answer is the vendor's own 404
refuse — each of these reads reports on an export or import that a WRITE would have started, and writes are refused; GitLab answers 404 for an export nobody requested
refuse — runner managers, controllers, tokens and router discovery describe physical CI machines and their credentials, not the org; GitLab restricts them to admins
generate — canon has no SecurityFinding/SbomComponent/Attestation; a scanning org has findings
the calling persona has no pending to-do — canon carries no per-person work queue and every review request it would echo is already served at /merge_requests; GitLab answers `[]` at 200 for a caller with an empty queue. THE LIST HALF, served as GitLab's empty page with the pagination headers the operation declares. UNGATED: canon carries no per-person work queue in any generation, so every registered pin answers this same `[]`. Falsifiable: the `expectsEmpty` conformance entry reds the row the day canon grows one. RULED by the founder 2026-09-12
refuse — no commit or tag in this universe is GPG-signed, and GitLab answers its own 404 'GPG Signature Not Found' for an unsigned object
03 / What's simulated
Every GitLab call resolves against the same simulated data set every other provider serves. Counted from the gl-v4-g11 artifact (universe generation 11):
Each line below is one assertion in the conformance suite named beside it — run on every build, over one artifact, through the real renderers. Nothing is claimed here that no expect checks.
Every merge request's `source_branch` (and GraphQL `sourceBranch`) is the same string GitHub serves as `head.ref`, Bitbucket as `source.branch.name` and Azure DevOps as `refs/heads/…`, and it resolves as a real branch.
Against GitHub, Bitbucket, Azure DevOps · parity/branch-parity.test.ts
Milestone `iid`s and their created/updated instants are identical to GitHub's milestone numbers and instants, on the live generation and on the frozen-pin generation both.
Against GitHub · parity/milestone-parity.test.ts
The group's `created_at` is the same instant GitHub's `/orgs/{org}`, Linear's `organization.createdAt` and the Zendesk account's locale records return; every group member's `created_at` is that member's first-seen instant, and never later than a pull request or issue GitHub renders them as authoring.
Against GitHub, Linear, Zendesk · parity/org-member-parity.test.ts
The merge request at the end of the four-hop walk carries the same `source_branch` and the same 40-character `sha` the GitHub pull request served, for a story that started as a Slack thread and a Jira/Linear issue — on the live hosts and on the frozen `-g6` pins.
Against Slack, Jira, Linear, GitHub · parity/pinned-cross-category.test.ts
04 / Known deviations
These answer with real data and match what GitLab actually returns, but fail the spec we vendor from GitLab — nearly always a bug in that published description. They are listed rather than counted so you can see whether one of them is the endpoint your client calls, and they are never counted as verified.
GET /api/v4/groups/{id}/access_tokensdeploy-keys-tokensSERVED 2026-09-10 (hello-16): reader.listCredentials("group-token") filtered on scope_ref (reader.ts:3085) — one row, the roster-sync token scoped to the organization's own group. THE SUBGROUP RESOLVES AND ANSWERS []: it owns no repository and has no automation to run (founder ruling F1, 2026-09-09), and conformance drives both ids. `access_level` is the holder's own organization standing, the same derivation the project rows use. Unpaginated, per the vendored operation. Gated on hasCredentials()GET /api/v4/groups/{id}/access_tokens/{token_id}deploy-keys-tokensSERVED 2026-09-10 (hello-16): the same row by id, byte-identical to the list's first entry. The organization's token id does NOT resolve under the subgroup — conformance asserts it, because a token belongs to one group rather than to every group on the hostGET /api/v4/personal_access_tokensdeploy-keys-tokensSERVED 2026-09-10 (hello-16): reader.credentialsForPerson(athena, "personal-token") (reader.ts:3075) — "all personal access tokens accessible by the authenticated user", which on a NON-ADMINISTRATOR is their own, and athena is who GET /user reports. A TOKEN IS METADATA AND NEVER A VALUE: name, scopes, creation, expiry, last use and state, because a provider shows a token's value once at creation and there is no creation here (the PagerDuty routing-key rule). Canon's neutral TokenScopes map to GitLab's coarser vocabulary in one place (render-credentials.ts) and the mapper THROWS on a scope it has never heard of rather than dropping it. `expires_at` is GitLab's DATE form — its own docs example is "2021-01-31" while created_at/last_used_at beside it are timestamps — which the vendored document mistypes date-time, so the row is a deviation. `last_used_ips` is omitted: an IP means something outside this universe and canon records none. Gated on hasCredentials()GET /api/v4/personal_access_tokens/{id}deploy-keys-tokensSERVED 2026-09-10 (hello-16): the caller's own tokens by id, byte-identical to the list's first row. Another member's token id answers GitLab's 404 — the same non-administrator visibility the list reports, read from the other end, and conformance drives itGET /api/v4/personal_access_tokens/selfdeploy-keys-tokensSERVED 2026-09-12 (hello-17): `self` names the token the REQUEST AUTHENTICATED WITH, and hello-17 gives the caller one that works: the founding member holds a personal token by construction and the setup-token rotation keeps it live, so the row answers an ACTIVE token rather than the expired one that would have described a request GitLab would have answered 401. The PENDING request canon also carries can never surface here - credentialScope() excludes state='pending' from every list accessor, and credentialRequests() is the only place a request is reachableGET /api/v4/projects/{id}/access_tokensdeploy-keys-tokensSERVED 2026-09-10 (hello-16): reader.listCredentials("project-token") filtered on scope_ref (reader.ts:3085) — TWO ROWS, AND THEY ARE THE ROTATION: the CI token in force when the incident opened was revoked at the incident's first instant and a replacement minted in the same breath, so the revoked row's ended_epoch IS the active row's created_epoch (conformance asserts they abut). `user_id` is the principal the token acts as — this universe's CI identity, a person a client can then fetch, where real GitLab mints a bot user. `access_level` is that holder's OWN standing in the repository, read off reader.membershipFor("repo", …) and mapped once by GITLAB_ACCESS_LEVEL: GitLab's own rule is that a resource token cannot exceed its creator's role. Unpaginated, because the vendored operation declares neither page nor per_page. Gated on hasCredentials()GET /api/v4/projects/{id}/access_tokens/{token_id}deploy-keys-tokensSERVED 2026-09-10 (hello-16): the same two rows by id, byte-identical to the list's first entry. A DEPLOY token's id does NOT resolve here and conformance asserts it — GitLab keeps the two families in separate tables and so does canonGET /api/v4/projects/{id}/deployments/{deployment_id}/merge_requestsdeploy-keys-tokensSERVED 2026-09-12 (coverage wave W8): the deployment names a commit and reader.pullsForCommit resolves the merge requests that commit belongs to — the ones listing it among their commits plus the one whose MERGE commit it is, which is the shape every staging deployment in this universe takes. THE SAME READ backs the DORA lead-time fold, so a lead time and this list cannot disagree about which changes went out. The rows are `glMergeRequest`'s own, carrying the iid `/merge_requests/{iid}` answers to — conformance fetches each one by that iid and asserts it is the same merge request — and the same nullable-key `deviation` the merge-request list already declares. A deployment id nobody has is GitLab's own 404GET /api/v4/users/{user_id}/project_deploy_keysdeploy-keys-tokensSERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): canon.CheckoutKey IS a deploy key — `CheckoutKeyType` is literally "deploy-key" — and reader.listCheckoutKeys(repo) already carries the OpenSSH line and BOTH fingerprints in the forms GitLab prints them. `title` is the comment at the end of that same line, READ OFF the material rather than kept as a second copy, so the title and the key can never name two different things; `created_at` is created_epoch; `fingerprint` is the colon-hex MD5 and `fingerprint_sha256` the OpenSSH form. `expires_at`, `last_used_at` and `usage_type` are OMITTED rather than nulled: this universe issues no key with an expiry, records no use of one and stores no usage flag, all three are optional, and a null typed `string` would buy a deviation for nothing. GENERATION-GATED: an artifact without the CI-configuration domain answers that gap rather than `[]`, because `[]` would say this project's CI clones with nothing. `Entities::DeployKey` RATHER THAN `DeployKeysProject`, which is why this row alone publishes the two project arrays and carries no `can_push`: the whole point of a user-scoped key list is saying WHICH projects each key reaches. `projects_with_write_access` is EMPTY and the project sits under `projects_with_readonly_access`, because `can_push` is false on these keys — listing the project as a write grant while the project-scoped row says the key cannot push would be this host contradicting itself about one key (invariant #5). Both arrays are a `deviation`, the vendored component typing each as a single object while real GitLab sends a collection. The PERSON still has to resolve, so an id nobody holds is GitLab's own 404 rather than a list about nobodyGET /api/v4/groups/{id}/merge_requestsmerge-requests"all merge requests for a specified group AND ANY SUBGROUPS", which for the organization is every merge request in the universe. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers `200 []`: it owns no project and has no subgroup below it (founder ruling F1)GET /api/v4/merge_requestsmerge-requestsGET /api/v4/projects/{id}/approval_rulesmerge-requestsSERVED 2026-09-12 (hello-17): features.reviewPolicy gives the repository the rule its merges were actually held to - approvals_required MEASURED off the merged pulls, approvers derived from the people the review log records approving. rule_type is `regular` by GitLab's own definition (a rule that names its approvers), eligible_approvers IS users because `groups` is empty, and `groups` is empty as a fact about GITLAB'S MODEL rather than about the policy: canon's approver TEAMS have no GitLab container on this host, which publishes exactly two groups and neither is a team. applies_to_all_protected_branches is true because canon's rule carries no branch filter; the vendored component types the accompanying protected_branches as a single object where real GitLab sends an array, so the row is a deviationGET /api/v4/projects/{id}/approval_rules/{approval_rule_id}merge-requestsSERVED 2026-09-12 (hello-17): the by-id twin of the project's approval-rule list, byte-identical to the same rule inside itGET /api/v4/projects/{id}/approval_settingsmerge-requestsSERVED 2026-09-12 (hello-17): the private settings surface over the same rule, rendered by the same shaper as /approval_rules so the two cannot drift. fallback_approvals_required is the rule's own number because canon carries ONE approval measurement for a repository - what its merges actually carried - and there is no second figure for a fallback to differ by. `target_branch` narrows nothing and is answered rather than refused, because the rule applies to every protected branch. The vendored component types `rules` as a single object where real GitLab sends an array, so the row is a deviationGET /api/v4/projects/{id}/approvalsmerge-requestsSERVED 2026-09-12 (hello-17): the project's approval configuration: approvals_before_merge is the rule's own requirement and disable_overriding_approvers_per_merge_request is canon's `optional` read from the other end. `approvers` and `approver_groups` are the empty arrays real GitLab sends on any instance that uses approval RULES, which this one does - not a gap - and the vendored components type both as single objects, so the row is a deviation. The seven policy toggles beside them (reset-on-push, author approval, committer approval, password to approve) are OMITTED rather than defaulted: canon models no such policy and a chosen false would be a placeholder wearing a boolean's clothesGET /api/v4/projects/{id}/merge_requestsmerge-requestsGET /api/v4/projects/{id}/merge_requests/{merge_request_iid}merge-requestsGET /api/v4/projects/{id}/merge_requests/{merge_request_iid}/changesmerge-requestsGET /api/v4/projects/{id}/merge_requests/{merge_request_iid}/closes_issuesmerge-requestsGET /api/v4/projects/{id}/merge_requests/{merge_request_iid}/time_statsmerge-requestsno persona in olympus-labs logs time against an issue or a merge request — canon records no TimeEntry; GitLab's own answer for an untracked issue is a 200 carrying `time_estimate: 0`, `total_time_spent: 0` and both human_ fields null. NOT A COLLECTION: the operation returns `APIEntitiesIssuableTimeStats`, an object, so the honest empty is the zeroed object rather than `[]`. It is the SAME object `glIssue` and `glMergeRequest` already embed as `time_stats` (`emptyTimeStats` in packages/renderer-gitlab/src/render2.ts), read from one function so the standalone endpoint and the embedded block can never disagree (invariant #5). canon's `Issue.estimate` is a story-point estimate on a fibonacci scale rather than a duration, and `time_estimate` is documented in seconds, so mapping one onto the other would be an invention. The two human_ fields are a `deviation` against the vendored component, which types them `string` with no `nullable` while real GitLab renders nil for a zero — the same two entries NULLABLE_SPEC_BUGS already carries for the embedded block. UNGATED: no generation of this universe records a time entry. RULED by the founder 2026-09-12GET /api/v4/groups/{id}/billable_members/{user_id}/indirectgroupsSERVED 2026-09-12 (hello-17): WAS `empty` AND THE RULING WAS FALSIFIED ON SCHEDULE. The 2026-09-09 namespaces decision said in as many words that the day canon gives a namespace a member this goes red rather than hiding it; features.grants gives the subgroup a roster of five - the people whose authority is over the PLAN rather than over the code - so this route serves that person's namespace membership, with created_at read off the membership row's own instant rather than the person's first sighting. A person on no namespace roster still gets [], which is what keeps this a derive rather than a claim that everybody is in the subgroup. THE SUBGROUP STILL ANSWERS GITLAB'S OWN 404: the vendored document says the operation works on top-level groups only. On every artifact without the domain namespaceMembers() finds nothing and the answer is the [] all 127 registered pins serveGET /api/v4/groups/{id}/billable_members/{user_id}/membershipsgroupsthe memberships that put one person on a seat: the group, then every project it owns — the same two levels /users/{id}/memberships reports. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers GitLab's 404, with the rest of the billable-members family: it addresses a billable member OF a group whose own billable list does not exist for a subgroup, and a member of a list that 404s cannot resolve (invariant #5 from the other end)GET /api/v4/groups/{id}/descendant_groupsgroupsthe transitive twin of the subgroup list, from the same reader.listNamespaces walk. One level deep in this universe, so it answers the same single row; older pins still answer the empty list. SERVED 2026-09-09 (hello-16)GET /api/v4/groups/{id}/invitationsgroupsSERVED 2026-09-12 (coverage wave W3): the OUTSTANDING invitations on this group - reader.listMemberships('org', org) restricted to state pending with no person, an invitee_email and no failed_epoch. GitHub serves exactly these rows at /orgs/{org}/invitations, so the two hosts name the same outstanding offers. A LAPSED offer is excluded and the exclusion is asserted: canon marks an expired invitation with failed_epoch and GitHub publishes those separately, while GitLab has no failed-invitation surface at all - listing one as pending would tell a client an offer can still be accepted when it cannot. `invite_token` is omitted and always will be: it is the secret a recipient redeems the invitation with, and canon's no-secret contract means no row carries one. `user_name` is omitted because every invitation here was sent to an ADDRESS with no account behind it. `access_level` is the integer real GitLab sends where the component types it string, so the row is a deviation. The subgroup resolves and answers its own set. The old reason - a new canon entity Invitation is needed - was falsified by hello-16's membership domain, which models an invitation as a pending membership with an addressGET /api/v4/groups/{id}/issuesgroupsevery issue in the organization's projects. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers `200 []`: it owns no repository (founder ruling F1, 2026-09-09) and has no subgroup of its own, so the rollup is empty BY CONSTRUCTION rather than by a gap — the artifact asserts `repo.namespace IS NULL` on every row and conformance re-checks itGET /api/v4/groups/{id}/subgroupsgroupsfeatures.namespaces (hello-16) gives this organization one subgroup, so reader.listNamespaces(parent) answers a real child. The 2026-09-09 empty said a subgroup was on hello-16 and this is that generation; older pins still answer the empty list, which is what they truthfully hold. SERVED 2026-09-09 (hello-16)GET /api/v4/projects/{id}/issuesprojectsGET /api/v4/projects/{id}/issues/{issue_iid}projectsGET /api/v4/projects/{id}/issues/{issue_iid}/closed_byprojectsGET /api/v4/projects/{id}/share_locationsprojectsthe groups a project can be invited to, excluding the one that already owns it. The subgroup qualifies from hello-16. Unpaginated: the vendored document declares no page/per_page on this operation. Older pins still answer the empty list. SERVED 2026-09-09 (hello-16)GET /api/v4/groups/{id}/epics/{noteable_id}/discussionsdiscussionsSERVED 2026-09-12 (hello-17): a discussion IS the epic's notes grouped by entity_note.thread_root - not a second entity and not a second query - so /notes and /discussions partition exactly the same set and render each note identically. individual_note is `this thread has one note`; resolvable is false because GitLab makes only DIFF notes resolvable and an epic has no diff. The vendored component types `notes` as a single object where real GitLab sends an array, so the row is a deviation, exactly as its issue and merge-request twins areGET /api/v4/groups/{id}/epics/{noteable_id}/discussions/{discussion_id}discussionsSERVED 2026-09-12 (hello-17): one epic discussion by its 40-hex digest, which is SHA-1 over the canonical id of the thread's root note - the same derivation the issue and merge-request discussions use, so a discussion is addressable by exactly one thread. A discussion that belongs to a different epic 404s here rather than being served under the wrong parentGET /api/v4/projects/{id}/issues/{noteable_id}/discussionsdiscussionsGET /api/v4/projects/{id}/issues/{noteable_id}/discussions/{discussion_id}discussionsGET /api/v4/projects/{id}/merge_requests/{noteable_id}/discussionsdiscussionsGET /api/v4/projects/{id}/merge_requests/{noteable_id}/discussions/{discussion_id}discussionsGET /api/v4/user/emailsusersSERVED 2026-09-12 (coverage wave W3): reader.credentialsForPerson(athena, 'email') - athena is the caller GET /api/v4/user reports, and GitHub's /user/emails has been green off the same person_credential rows since the hello-16 credential wave. `confirmed_at` is the credential's own instant and is sent only on an ACTIVE row: GitLab's field records when the address was confirmed to belong to its owner, canon records when the person came to hold it, and a row canon ever revokes loses the field in the same edit rather than keeping a confirmation that is no longer true. `id` is the integer real GitLab sends where the vendored component types it string, so the row is a deviation. Behind the hello-16 credential gate, not the hello-17 caller gate: the caller has held an address since hello-16, which is a different question from whether she holds a key. The old reason - canon's person carries no email list - was falsified by that generationGET /api/v4/user/emails/{email_id}usersSERVED 2026-09-12 (coverage wave W3): the by-id twin of /api/v4/user/emails, addressed by gitlabId(credential.id); conformance asserts the list and the by-id row render one address the same way. Same hello-16 gate, same `id` deviation, same falsified reasonGET /api/v4/user/statususersnobody in olympus-labs sets a status message — canon's person carries no availability, emoji or status text; GitLab's own answer for a user who has set none is a 200 with every field null. NOT A COLLECTION: `APIEntitiesUserStatus` is an object, so the honest empty is the all-null object rather than `[]`. All five fields are a `deviation` against the vendored component, which types each of them `string` with no `nullable` while GitLab renders `Entities::UserStatus` over a person who has set no status and every field comes out nil. UNGATED: `canon.Person` (packages/canon/src/entities.ts:79) has carried id, archetype, teams, utcOffsetMinutes, isBot and member in every generation and has grown only `aiTool`/`extraAiTools` since, so no artifact of this universe has ever held an availability, an emoji or a status text. RULED by the founder 2026-09-12GET /api/v4/users/{id}/associations_countusersGET /api/v4/users/{user_id}/membershipsusersGET /api/v4/users/{user_id}/statususersnobody in olympus-labs sets a status message — canon's person carries no availability, emoji or status text; GitLab's own answer for a user who has set none is a 200 with every field null. NOT A COLLECTION: `APIEntitiesUserStatus` is an object, so the honest empty is the all-null object rather than `[]`. All five fields are a `deviation` against the vendored component, which types each of them `string` with no `nullable` while GitLab renders `Entities::UserStatus` over a person who has set no status and every field comes out nil. UNGATED: `canon.Person` (packages/canon/src/entities.ts:79) has carried id, archetype, teams, utcOffsetMinutes, isBot and member in every generation and has grown only `aiTool`/`extraAiTools` since, so no artifact of this universe has ever held an availability, an emoji or a status text. RULED by the founder 2026-09-12GET /api/v4/groups/{id}/-/epicsepicsEpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)GET /api/v4/groups/{id}/-/epics/{epic_iid}epicsEpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)GET /api/v4/groups/{id}/-/epics/{epic_iid}/issuesepicsEpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)GET /api/v4/groups/{id}/(-/)epics/{epic_iid}/epicsepicsthe CHILD epics of an epic - GitLab's Epics::EpicLinks, which the vendored summary labels "related epics" while its POST twin says "relate epic to a PARENT". epic.parent/level are canon from hello-16 (features.portfolio) and reader.epicChildren(id) answers it directly; a leaf reports the empty array and an older pin the noEpics generation-gap 404. The 2026-09-01 reason was right that parentage was not modelled and is no longer true; the SIBLING relation it also names still is not, which is why groups/{id}/epics/{epic_iid}/related_epics stays generate. SERVED 2026-09-09 (hello-16)GET /api/v4/groups/{id}/epicsepicsEpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)GET /api/v4/groups/{id}/epics/{epic_iid}epicsEpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)GET /api/v4/groups/{id}/epics/{epic_iid}/issuesepicsEpicRow carries the portfolio columns from hello-16 (features.portfolio): author, state, description, start/due/closed epochs and one level of parent/child. reader.hasPortfolio() + listEpics()/epic()/epicChildren()/epicIssues() derive GitLab's Epic entity; older pins answer the noEpics generation-gap 404 rather than an empty plan. SERVED 2026-09-09 (hello-16)GET /api/v4/projects/{id}/pipeline_schedules/{pipeline_schedule_id}pipelinesSERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): reader.listWorkflowSchedules(repo) over canon.WorkflowSchedule. `description` and `ref` are the schedule's own description and branch, `owner` is the actor it runs as, `created_at` and `updated_at` its two epochs, `cron_timezone` UTC because canon's hours ARE UTC, and `cron` is the (per_hour, hours_of_day, days_of_week) triple re-encoded LOSSLESSLY — every value in the triple appears in the expression and nothing else does, which conformance checks field by field rather than against a literal. `active` is true because every schedule here really starts runs, and an inactive schedule that had started 398 pipelines would contradict its own pipeline list. `next_run_at` is walked forward from THE ARTIFACT'S ANCHOR, never Date.now(): a frozen pin must print the same instant in a year's time. `inputs` is omitted — canon carries no schedule input. GENERATION-GATED: an older artifact answers the CI-configuration gap rather than `[]`, which would say nothing here runs unattended. The detail shape adds `last_pipeline`, and it is the newest run THIS SCHEDULE started (reader.runsForSchedule) rather than the project's newest pipeline — conformance asserts every other field is byte-identical to the list row's. VARIABLES SERVED 2026-09-13 (hello-19 `ciBindings`), AND THIS ROW IS A `deviation` BECAUSE OF THEM: the detail shape also publishes the schedule's CI/CD variables, off the SAME glScheduleVariables the by-key row /pipeline_schedules/{id}/variables/{key} reads, so the two surfaces cannot disagree about one fact — conformance compares the block to that URL's bytes element for element. A masked binding renders `masked: true, hidden: true` and ships NO `value` key, here as everywhere: no ci_binding row carries a value when masked = 1 (compiler/src/hello19.test.ts asserts it as SQL over every compiled artifact). THE DEVIATION IS THE VENDORED DOCUMENT'S: APIEntitiesCiPipelineScheduleDetails types `variables` as a SINGLE APIEntitiesCiVariable object while real GitLab sends an ARRAY — `Ci::PipelineSchedule has_many :variables`, `Entities::Ci::PipelineScheduleDetails` exposes them `using: Entities::Ci::Variable` over that association, and GitLab's own published example for this operation shows `"variables": [{...}]`. Invariant #2 mirrors the provider rather than its documentation, so the array is served and the one validator error is declared a spec bug (SCHEDULE_VARIABLES_SPEC_BUGS); the vendored file is upstream-verbatim and sha256-pinned, so it is NOT edited — the correction and the evidence live in conformance and in coverage/provider-pins.yaml. Same class as `approved_by` on the code-review component. The `variables` KEY IS ABSENT rather than `[]` on a pre-hello-19 artifact: [] would say this organization hands its nightly release nothing while the same artifact publishes a CI document, a schedule that starts real runs and deployments to two environments, and a whole-response coverage 404 would break invariant #2 on all 127 frozen pins, which fetch this row and get GitLab's 200. gitlab/hello19.test.ts asserts the row gains exactly that one key and nothing elseGET /api/v4/projects/{id}/pipelines/{pipeline_id}/test_reportpipelinesSERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): reader.testResultsForJob(job) over canon.TestCase/TestResult, folded PER JOB — GitLab keys a test report's suites by the BUILD that reported them, which is what makes one suite per job the mirror rather than a choice. canon spells a failure "failure" and GitLab spells it "failed"; the other three statuses agree. `system_output` is the reporter's message and is absent on a pass, because canon stores NULL there rather than an empty string. `file`, `stack_trace`, `recent_failures` and `attachment_url` are omitted — canon records none of them. The durations are FLOAT seconds as real GitLab sends them while both vendored components type them `integer`, so the row is a `deviation` rather than rounding canon's millisecond durations away. Generation-gated: an older artifact answers the CI-configuration gap rather than a report of no tests. Conformance recomputes every counter from the artifact and asserts each served case is one canon DECLARES, with canon's own classname — a test name invented here would be a test nobody wroteGET /api/v4/projects/{id}/pipelines/{pipeline_id}/test_report_summarypipelinesSERVED 2026-09-12 (coverage wave W8) (hello-15 `ciConfig`): reader.testResultsForJob(job) over canon.TestCase/TestResult, folded PER JOB — GitLab keys a test report's suites by the BUILD that reported them, which is what makes one suite per job the mirror rather than a choice. canon spells a failure "failure" and GitLab spells it "failed"; the other three statuses agree. `system_output` is the reporter's message and is absent on a pass, because canon stores NULL there rather than an empty string. `file`, `stack_trace`, `recent_failures` and `attachment_url` are omitted — canon records none of them. The durations are FLOAT seconds as real GitLab sends them while both vendored components type them `integer`, so the row is a `deviation` rather than rounding canon's millisecond durations away. Generation-gated: an older artifact answers the CI-configuration gap rather than a report of no tests. The SAME fold, totals only: the summary carries `build_ids` and no `test_cases`, which is the whole difference between the two operations and the reason both exist. Conformance asserts the two endpoints agree counter for counter — two different totals for one pipeline would be this host describing two universes of tests. `test_suites` is a `deviation`, the vendored component typing the collection as one objectGET /api/v4/groups/{id}/milestonesmilestonesfeatures.namespaces (hello-16) puts the release train the repository's milestone rolls up into on the subgroup; reader.milestonesForNamespace(id) derives it with group_id and its own iid. Older pins answer the empty list. SERVED 2026-09-09 (hello-16)GET /api/v4/groups/{id}/milestones/{milestone_id}milestonesthe by-id twin of the group milestone list, off the same reader.milestonesForNamespace row; an id nobody holds is still GitLab's 404 Milestone Not Found, and that is the whole answer on every pin older than hello-16. SERVED 2026-09-09 (hello-16)GET /api/v4/projects/{id}/milestonesmilestonesGET /api/v4/projects/{id}/milestones/{milestone_id}milestonesGET /api/v4/projects/{id}/milestones/{milestone_id}/issuesmilestonesGET /api/v4/projects/{id}/repository/commits/{sha}/merge_requestscommitsGET /api/v4/issuesissuesGET /api/v4/issues/{id}issuesGET /api/v4/projects/{id}/issues/{issue_iid}/linksissuesSERVED 2026-09-12 (hello-17): features.issueLinks derives the edges from the storyline - issues in one storyline relate, and an issue whose close precedes another's start blocks it. Canon stores the link ONCE, directed; this route renders it relative to the issue you asked about, so one `blocks` row reads `blocks` from the blocker's end and `is_blocked_by` from the blocked one's. The other end is the same issue /issues/{iid} serves, shaped by the same glIssue, plus the four link fields APIEntitiesRelatedIssue adds; sorted by the relationship's own instant ascending, which is the vendored document's own ordering. Answers the named hello-17 generation gap on every older artifactGET /api/v4/projects/{id}/issues/{issue_iid}/links/{issue_link_id}issuesSERVED 2026-09-12 (hello-17): one link by id, naming both ends outright - so link_type here is the CANONICAL direction rather than the relative one /links prints. Looked up inside this issue's own links, so a link between two other issues 404s rather than being served under a parent it does not touchGET /api/v4/projects/{id}/issues/{issue_iid}/related_merge_requestsissuesGET /api/v4/projects/{id}/issues/{issue_iid}/time_statsissuesno persona in olympus-labs logs time against an issue or a merge request — canon records no TimeEntry; GitLab's own answer for an untracked issue is a 200 carrying `time_estimate: 0`, `total_time_spent: 0` and both human_ fields null. NOT A COLLECTION: the operation returns `APIEntitiesIssuableTimeStats`, an object, so the honest empty is the zeroed object rather than `[]`. It is the SAME object `glIssue` and `glMergeRequest` already embed as `time_stats` (`emptyTimeStats` in packages/renderer-gitlab/src/render2.ts), read from one function so the standalone endpoint and the embedded block can never disagree (invariant #5). canon's `Issue.estimate` is a story-point estimate on a fibonacci scale rather than a duration, and `time_estimate` is documented in seconds, so mapping one onto the other would be an invention. The two human_ fields are a `deviation` against the vendored component, which types them `string` with no `nullable` while real GitLab renders nil for a zero — the same two entries NULLABLE_SPEC_BUGS already carries for the embedded block. UNGATED: no generation of this universe records a time entry. RULED by the founder 2026-09-12GET /api/v4/groups/{id}/(-/)searchsearchscope-dispatched search over the group's projects. THE SUBGROUP RESOLVES HERE as of 2026-09-10 and answers the empty result set for every scope, because a group search is a search over its PROJECTS and it owns none (founder ruling F1). A missing or invalid `scope` is still GitLab's own 400 on either idGET /api/v4/projects/{id}/(-/)searchsearchGET /api/v4/projects/{id}/analytics/deployment_frequencyanalyticsSERVED 2026-09-12 (coverage wave W8): DORA's PREDECESSOR on this host, and a different shape — `{value, from, to}` per bucket rather than `{date, value}` — folded over the same reader.listDeployments rows, with `environment` and `from` both required as the vendored operation declares. `value` is a `deviation`: the component types it `string` while real GitLab sends the integer count. The clock is the artifact's anchor, for the reason the DORA rows giveGET /api/v4/groups/{id}/saml/{uid}provider identitiesSERVED 2026-09-12 (hello-18): one identity by the handle itself — THE ROW THE WHOLE DOMAIN WAS ASKED FOR, since a uid that resolves to a person instead of to nothing is what this clique has named as its blocker since 2026-09-09. Byte-identical to that person's entry in the list, which conformance compares directly; a handle of the right SHAPE that nobody holds is GitLab's own 404, so the refusal is about the value rather than about the path. Same deviation as its list. The subgroup resolves the family — the document restricts it to no particular group — and answers [] for a list and GitLab's 404 for a named read, because a subgroup federates nobody; the organization above it does. Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)GET /api/v4/groups/{id}/saml/identitiesprovider identitiesSERVED 2026-09-12 (hello-18): the directory identities themselves — APIEntitiesIdentityDetail over person.external_uid, a 32-character lowercase hex handle (a UUID with its hyphens removed, which is the width Entra and Okta issue) that is a pure function of the root seed and the person's id. DEVIATION RATHER THAN GREEN, and the evidence is the vendored document contradicting itself: APIEntitiesIdentityDetail types `user_id` and `active` as `string`, while GitLab's own published example for these operations is {"extern_uid": "4", "user_id": 48, "active": true} — an integer and a boolean. Invariant #2 mirrors the provider rather than its documentation, so the real types are served and the two errors are declared spec bugs. `active` is true on every row because canon records no deprovisioning. Conformance asserts every handle matches /^[0-9a-f]{32}$/, that no two people share one, and that every user_id resolves to a person this host serves. The subgroup resolves the family — the document restricts it to no particular group — and answers [] for a list and GitLab's 404 for a named read, because a subgroup federates nobody; the organization above it does. Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)GET /api/v4/groups/{id}/scim/{uid}provider identitiesSERVED 2026-09-12 (hello-18): one SCIM identity by the handle, byte-identical to the SAML identity for the same handle — one directory, one identity, asserted rather than assumed. Same deviation as its list. The subgroup resolves the family — the document restricts it to no particular group — and answers [] for a list and GitLab's 404 for a named read, because a subgroup federates nobody; the organization above it does. Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)GET /api/v4/groups/{id}/scim/identitiesprovider identitiesSERVED 2026-09-12 (hello-18): the directory identities themselves — APIEntitiesIdentityDetail over person.external_uid, a 32-character lowercase hex handle (a UUID with its hyphens removed, which is the width Entra and Okta issue) that is a pure function of the root seed and the person's id. DEVIATION RATHER THAN GREEN, and the evidence is the vendored document contradicting itself: APIEntitiesIdentityDetail types `user_id` and `active` as `string`, while GitLab's own published example for these operations is {"extern_uid": "4", "user_id": 48, "active": true} — an integer and a boolean. Invariant #2 mirrors the provider rather than its documentation, so the real types are served and the two errors are declared spec bugs. `active` is true on every row because canon records no deprovisioning. THE SCIM IDENTITIES ARE THE SAME IDENTITIES, and that is what one directory means: canon holds a single identity_provider_config with scim_enabled = 1, so a second, different handle here would assert a second directory nobody configured. Conformance compares the two lists element for element. The subgroup resolves the family — the document restricts it to no particular group — and answers [] for a list and GitLab's 404 for a named read, because a subgroup federates nobody; the organization above it does. Gated on hasExternalUid(); an older artifact answers the named hello-18 identity gap rather than [], because an empty list there would say nobody here was provisioned by a directory while this same host publishes a SAML configuration with SCIM enabled — the replica disagreeing with itself (invariant #5)GET /api/v4/groups/{id}/manage/resource_access_tokensgroup credentials inventorySERVED 2026-09-10 (hello-16): "all group and project access tokens associated with a TOP-LEVEL-GROUP" — the group token plus every project token below it, from the same two reader.listCredentials calls, and conformance asserts the managed list is exactly the UNION of the narrower ones so a token cannot appear here without appearing on its own container. THE SUBGROUP GETS GITLAB'S 404 rather than a list: the document restricts the operation to a top-level group, and an empty list would report a different fact from the one the provider reports. Gated on hasCredentials()GET /api/v4/analytics/code_reviewcode review analyticsSERVED 2026-09-12 (coverage wave W8): GitLab's own definition of the review queue, in its own words — the OPEN merge requests that have at least one non-author comment, ordered by review time with the longest first. reader.listPulls + pullReviewComments + listReviews; a merge request nobody has reviewed is absent rather than listed with a null time, which is what makes the queue a queue. `review_time` is whole hours from the first non-author note to the NEWEST INSTANT this universe records against that merge request (`mrUpdatedEpoch`, the same value `updated_at` publishes, widened by the conversation's own last note and last review): a snapshot has a last recorded event rather than a wall clock, canon carries review comments that postdate the anchor, and anchoring the subtraction would print a negative duration for a merge request that is plainly under review. `approved_by` is the approving reviews' authors and is a `deviation`, the vendored component typing a collection as one object. `diff_stats` is OMITTED: canon's `pull` carries no line counts at all and inventing them would be a placeholder05 / Pinned snapshots
Each pin regenerates byte-identically on every request, so a test written against one never drifts. Generations are DIFFERENT universes, not versions of one — never swap a suffix expecting the same data.
gl-v4.snap.sandboxapis.devgeneration 1v4not in this generationgl-v4-g10.snap.sandboxapis.devgeneration 10v4servedgl-v4-g11.snap.sandboxapis.devgeneration 11v4servedgl-v4-g2.snap.sandboxapis.devgeneration 2v4servedgl-v4-g3.snap.sandboxapis.devgeneration 3v4servedgl-v4-g5.snap.sandboxapis.devgeneration 5v4servedgl-v4-g6.snap.sandboxapis.devgeneration 6v4servedgl-v4-g8.snap.sandboxapis.devgeneration 8v4servedgl-v4-g9.snap.sandboxapis.devgeneration 9v4servedNeed an endpoint that is not served yet?
Every row GitLab's manifest carries is on this page, so “not here” is an answer rather than a gap in the rendering. Tell us which path and which client, and it moves up the queue — the order is set by what people ask for.