Repository navigation
Commit 2bcd406
fix(metadata-protocol)!: the by-name /meta read answers a disabled package's non-object item as absent, as the list omits it (#24223)
Part of #24210 (it covers the by-name read and the dispatcher's doors;
the doors listed under "Stopped at the fence" stay open and are why this
does not close the card)
Clause-②: no (narrowing)
Implements triage 6239967422 on #24210 (absence for a non-object type;
`OBJECT_PACKAGE_DISABLED` stays the object answer) under claim
6240246860, dispatched by the `domain:engine` seat 1
(`session_01JtSQQ3EWsbNPzyYMug1tXy`). Rework round: seat ruling
6241893667 under the re-declared claim 6241900550 (items 4 and 5 below;
Q1, Q2 and Q4 settled A).
## What changed
Disabling a package already took its items out of every `GET
/meta/:type` list. The by-name read still served them. It now answers
them the way it answers a name with nothing behind it.
1. **`getMetaItem`** (`packages/metadata-protocol/src/protocol.ts`).
After the body read (`readMetaItem`, unchanged), an item of any type
except `object` and `package` whose `_packageId` names a disabled
package is answered as absent:
- the active read and `previewDrafts` serve no `item`, and both
transports answer that `404 RESOURCE_NOT_FOUND`, byte for byte the
answer for a missing name;
- the strict draft read throws the missing draft's `404 NO_DRAFT` (one
constructor, `noPendingDraftError`, for both);
- a flow's `registration` is dropped with the item;
- the protection envelope (`lock`, `editable`, `deletable`, `version`
and the rest) stays. It reports the write doors' verdicts about the
address, and a disable does not change how the lock gate judges a save
there. The CLI ownership ceremony gates its own raw-SQL writes on
exactly these fields (`lock.verdict`), so this keeps a locked item
locked for it.
2. **One predicate (H1).** `isItemOfDisabledPackage(registry, item)` is
the list read's filter (`readFlattenedMetaItems`) and the by-name read's
check. Both ask `registry.isPackageDisabled(item._packageId)` of the
item the read resolved, so they hide the same items. There is no new
public method, spec key or error code.
3. **The dispatcher** (`packages/runtime/src/domains/meta.ts`,
cross-lane, declared to `domain:cli`). Four doors read past the protocol
and served a disabled package's object or item:
- `/meta/object/NAME/state/FIELD` and the legacy one-segment
`/meta/NAME` exit read `registry.getObject`. Both now ask
`disabledObjectAnswer` first, which answers the protocol's own `404
OBJECT_PACKAGE_DISABLED`;
- `/meta/:type/:name/published` swallowed the layered read's refusal and
fell through to the published snapshot. `OBJECT_PACKAGE_DISABLED` is now
the answer, as on `RestServer`'s `/published`;
- the item read of a non-object type fell back to the metadata service
when the protocol served no item. The service knows nothing of a
disable, so the fallback now skips an item whose `_packageId` names a
disabled package (`isServiceItemOfDisabledPackage`, the same predicate).
4. **`fetchCurrentMetaDocument`** (`packages/rest/src/rest-server.ts`,
the class member only, nothing inside `registerMetadataEndpointsInner`;
ruling item 1). It is the current document the `/history`, `/audit` and
`/diff` doors' per-caller gate judges. With item 1 a disabled package's
item read as "no current document", so those doors served events, and a
view's or dashboard's diff, past the gate. When the plain read serves no
item but the layered read (which keeps serving every layer) still
resolves one, the member now throws the plain read's absence, `404
RESOURCE_NOT_FOUND`, which the doors' own catch renders. An item that is
really gone (every layer null, such as a deleted item) keeps today's
answer.
5. **`readServedPackageBinding`**
(`packages/plugins/plugin-security/src/security-catalog-echo.ts`, that
function only; ruling item 2). It is a resolution read, so it reads the
binding off the protection envelope's declared `packageId` (the served
document's `_packageId`, kept when the item is withheld) instead of
`item._packageId`. A disabled package's permission set or position
echoes owned, and stays owned after a re-enable. No new public method,
no enforcement change.
`packages/rest/src/meta-item-read-gate.ts` is not edited.
## Premise: the callers of a by-name read of a non-object item
Measured before any edit. "Today" is `origin/main` `b7f29ddfe`; "after"
is this branch. The case is an item of a disabled package. objectui is
read at `origin/main` `54e79f9` (read-only clone). No caller has to tell
"disabled" from "missing", and none must be served the item, so absence
holds.
| Call site | What it does | Today | After |
|---|---|---|---|
| objectui `data-objectstack` `probeMetaItemAccess` and
`getAppAccessVerdict` (via `AppContent.tsx`, `useMetaItemRefusal.ts`) |
explains why an app, dashboard or view in the URL is not in the list |
`granted`, and `AppContent` shows the neutral "App not available"
screen: its own comment names this case, "the by-name route served the
app while the refreshed list still does not carry it" | `not_found`:
"This app can't be opened". Its verdict set has no disabled arm; denied
stays denied |
| objectui `data-objectstack` `getApp`, `getPage`, `getView`, the batch
`getItem(category, …)` | render-time reads | served | `null` (absence),
the face a missing item gets |
| objectui `MetadataProvider.getItem(type, name)`, `useActionModal`
(`page`), `FormPage.loadInternalForm` (`view`), `DocPage` / `DocsSlug`
(`doc`) | render the item | served | not found. Each uses the item, and
the list already omits it |
| objectui `DraftChangesPanel` (`/meta/TYPE/NAME` and `?state=draft`) |
diff a pending draft against its published side | both sides served |
both `404`, read as `null` sides. Not a reader that must be served: see
Acceptance notes on `_drafts` |
| objectui `PackagesPage`, `PackagedAutomationPage`,
`PackagedActionsSection` (Studio package screens, re-enable) |
`/api/v1/packages`, `_drafts`, lists | not a by-name reader | unchanged.
`package` is never hidden |
| objectui editors on `/layers` (`ResourceEditPage`,
`EmbeddedItemEditor`, `PermissionMatrixEditor`, `LayeredDiff`) | layered
view | served | unchanged here (`/layers` is a stopped door); every
entry point lists first |
| `packages/cli` ownership ceremony `lock.verdict`
(`organization-ownership.ts`), `PRESENTATIONAL_TYPES` = view, dashboard,
report, translation, email_template | gates raw-SQL writes on `editable`
/ `lock` | the item's real verdict | the same verdict: the envelope is
kept (pinned: "the protection envelope stays") |
| `packages/plugins/plugin-security` `readServedPackageBinding`
(`security-catalog-echo.ts`), `permission` / `position` | reads the
package that owns a stored overlay, for an environment-kernel echo | the
package id | the package id, read off the kept envelope (item 5; pinned
owned through disable and re-enable). `getMetaItemLayered`, which
decides whether the echo exists, is not touched |
| `packages/rest` `fetchCurrentMetaDocument` (`/history`, `/audit`,
`/diff`) | the current document the per-caller gate judges | the item |
absence: the doors answer `404 RESOURCE_NOT_FOUND` (item 4; measured
below) |
| `packages/rest` import mapping, `service-automation` connector pull
(`mapping`) | use the mapping | served | not found. Each uses the item |
| `packages/runtime` `applyPublishedSeeds`, `service-analytics` seed
draft probe (`seed`) | apply a seed / find a pending seed draft | served
| not found / `NO_DRAFT` (already caught). The seed's objects are
refused by the data plane anyway |
| `packages/core` import runner, `plugin-auth` admin user import, `rest`
data-action routes | read `type: 'object'` only | unchanged by this PR |
unchanged |
## Doors, measured over HTTP
**REST:** `pnpm dev:crm -- --fresh` (real socket), before at
`b7f29ddfe`, after at this branch with a full `pnpm build` (76/76).
**Dispatcher:** the real store (`SqliteWasmDriver`) and the real
protocol (`ObjectQLPlugin`) under `createHonoApp`, driven through its
fetch handler, the `packages/qa/http-conformance` harness shape (scratch
script, not committed). Both against a writable package
`app.probe-24210` holding object `probe_gadget`, app, page, dashboard,
flow and view.
| Request, package disabled | REST before | REST after | Dispatcher
before | Dispatcher after |
|---|---|---|---|---|
| `GET /meta/TYPE` contains the item (6 types) | no | no | no | no |
| `GET /meta/TYPE/NAME`, app, page, dashboard, flow, view | 200, served
| **404 `RESOURCE_NOT_FOUND`**, = missing name | 200, served | **404
`RESOURCE_NOT_FOUND`**, = missing name |
| `GET /meta/TYPE/NAME?preview=draft`, same six | 200, served | **404
`RESOURCE_NOT_FOUND`** | 200, served | **404 `RESOURCE_NOT_FOUND`** |
| `GET /meta/TYPE/NAME?state=draft`, same six | 404 `NO_DRAFT` | 404
`NO_DRAFT` | 404 `NO_DRAFT` | 404 `NO_DRAFT` |
| `GET /meta/TYPE/NAME?layers=true` / `/layers`, same six | 200, served
| 200, served (stopped) | 200, served | 200, served (stopped) |
| `GET /meta/TYPE/NAME/published`, same six | 200, served | 200, served
(stopped) | 200, served | 200, served (stopped) |
| `GET /meta/object/probe_gadget/published` | 404
`OBJECT_PACKAGE_DISABLED` | 404 `OBJECT_PACKAGE_DISABLED` | 404
`RESOURCE_NOT_FOUND` (snapshot fall-through; a host with a metadata
service serves the snapshot) | **404 `OBJECT_PACKAGE_DISABLED`** |
| `GET /meta/object/probe_gadget/state/status?from=open` | 200, served |
200, served (stopped) | 200, served | **404 `OBJECT_PACKAGE_DISABLED`**
|
| `GET /meta/probe_gadget` (one segment) | 400 `INVALID_REQUEST` (the
list route) | 400 | 400 `INVALID_REQUEST`, enabled and disabled alike |
400 |
| `GET /meta/object/probe_gadget`, `/data/probe_gadget` | 404
`OBJECT_PACKAGE_DISABLED` | unchanged | 404 `OBJECT_PACKAGE_DISABLED` |
unchanged |
| control: another package's object, by name and `/state` | 200 | 200 |
200 | 200 |
Byte equality: on REST the disabled item and a missing name both answer
`{"error":{"code":"RESOURCE_NOT_FOUND","message":"Metadata item not
found or access denied."}}`; on the dispatcher both answer
`{"success":false,"error":{"code":"RESOURCE_NOT_FOUND","message":"Not
found","httpStatus":404}}`. The enabled readings after are identical to
the enabled readings before on both transports, and re-enabling restores
every row. The REST server log carried 0 `failing open` warnings.
## Mechanism hypotheses (measured)
- **H1, one predicate: holds.** Every layer the by-name read serves
carries the owning package as `_packageId` on the resolved item: the
stored row's `package_id` (grafted in `readMetaItem`), the registry item
(stamped by `registerItem`), the artifact envelope. The list filter
reads the same field off its merged items, so one helper serves both.
- **H2, absence without a new path: holds.** The missing name's exit is
an item-less envelope, and both transports judge absence on `item ==
null` (`createMetaItemAnswer` on REST, the `data?.item != null` hit test
on the dispatcher); the cached arm throws `metadataItemNotFoundError` on
a falsy item, and REST maps both to `sendMetaItemAbsent`. The dispatcher
half needed the service-fallback guard above, because its miss fell
through to the metadata service.
- **H3, `/published`: half holds.** For objects, REST already re-throws
the layered refusal (`typeof status === 'number'`), measured 404
`OBJECT_PACKAGE_DISABLED` before any edit; the dispatcher's blanket
catch is now narrowed in `meta.ts`. For non-object types the producer
side cannot carry it: see below.
- **H4, the dispatcher's two doors: half holds.** `/state/FIELD` reads
the registry directly and served the object; closed. The one-segment
`/meta/NAME` legacy exit is not reachable over HTTP on a real
composition: `refuseUnknownMetaListType` answers 400 first, enabled and
disabled alike. It is reachable only on a host whose protocol declares
no type set; closed in code and pinned there.
## Stopped at the fence (open on #24210 for a follow-up dispatch after
this PR lands; #23050's move series is no longer in flight, notice
6243012505)
Ruling 6241893667 items 3, 4 and 6: these stay on the card, on the door
side, both transports together, after slice 13e.
1. **`?layers=true`, `/layers` and `/published` for a non-object item,
both transports.** Producer `getMetaItemLayered` keeps serving its
resolution reader, the security catalog echo
(`echoSecurityCatalogMutation`, `echoStoredSecurityCatalogOverlays`),
which turns a stored `permission` / `position` overlay into what an
environment kernel enforces; absence there would be an enforcement
change. The doors close on the door side (`rest-server.ts`'s layered and
`/published` handlers inside `registerMetadataEndpointsInner`, and
`meta.ts`'s twins). `/published`'s non-object fall-through is also
`MetadataManager.getPublished` (`packages/metadata`), whose document
carries no `_packageId`.
2. **REST `/meta/object/NAME/state/FIELD`, the interim transport
split.** REST's state route reads `ql.registry.getObject(name)` directly
and serves a disabled package's object (measured 200 after this PR); the
dispatcher twin answers 404 `OBJECT_PACKAGE_DISABLED`. The one-door
split holds until the REST half lands with item 1.
3. **`GET /meta/_drafts`** still lists a disabled package's drafts while
the by-name draft read answers `NO_DRAFT` for them: another door of the
same rule, joining item 1's follow-up.
## The event and diff doors, measured over HTTP (item 4)
`pnpm dev:crm -- --fresh`, real socket, full build after merging
`origin/main`. "Before" is this branch without item 4 (head `8946a02d4`,
the merge on top of `b2f89e8f`); "after" is `232339ab8`. A writable
package `app.gate-24210` holds dashboard `gate_dash` and app `gate_app`,
each saved twice, each with `requiredPermissions: ['gate_24210_cap']`;
dashboard `gate_gone` was saved and deleted.
- **Gated caller:** the seeded admin. It holds `studio.access` /
`manage_metadata`, so the three doors admit it, and not
`gate_24210_cap`, so the item's own gate refuses it the item whole: the
caller the bypass reaches.
- **Member caller:** a user created by `POST /auth/admin/create-user`
(no capabilities). The doors' capability gate refuses it first on every
row: `403 FORBIDDEN` on `/history`, `/audit`, `/diff`, enabled and
disabled, before and after.
| Gated caller, package state | Request | Before | After |
|---|---|---|---|
| enabled | `GET /meta/dashboard/gate_dash` and `/meta/app/gate_app` |
403 `PERMISSION_DENIED` | 403 `PERMISSION_DENIED` |
| enabled | `/history`, `/audit`, `/diff` of both | 403
`PERMISSION_DENIED` | 403 `PERMISSION_DENIED` |
| disabled | `GET /meta/dashboard/gate_dash` and `/meta/app/gate_app` |
404 `RESOURCE_NOT_FOUND` | 404 `RESOURCE_NOT_FOUND` |
| disabled | `/meta/dashboard/gate_dash/history` | **200, the events** |
**404 `RESOURCE_NOT_FOUND`** |
| disabled | `/meta/dashboard/gate_dash/audit` | **200, the audit
trail** | **404 `RESOURCE_NOT_FOUND`** |
| disabled | `/meta/dashboard/gate_dash/diff` | **200, the diff**
(`label` v1 to v2) | **404 `RESOURCE_NOT_FOUND`** |
| disabled | `/meta/app/gate_app/history`, `/audit` | **200, the
events** | **404 `RESOURCE_NOT_FOUND`** |
| disabled | `/meta/app/gate_app/diff` | 404 `RESOURCE_NOT_FOUND` | 404
`RESOURCE_NOT_FOUND` |
| deleted `gate_gone` | `/history`, `/diff` | 200 (events; empty diff) |
200 (unchanged) |
| re-enabled | every row | as enabled | as enabled |
Envelope position: the plain read answers
`{"error":{"code":"RESOURCE_NOT_FOUND","message":"Metadata item not
found or access denied."}}`; the event and diff doors now answer status
404 and code `RESOURCE_NOT_FOUND` in the classification door's flat
shape, `{"error":"Metadata item not found or access
denied.","code":"RESOURCE_NOT_FOUND"}`, because the member throws and
the doors' catch (`handleRouteError`) renders it. The app `/diff` row,
which answered the nested body before through the door's own absent
check, now answers the flat one. A byte-identical nested body needs the
door side (`eventDoorRefusal` and the `/diff` branch), outside this
fence.
## Pins
- `packages/objectql/src/package-disable-enforcement.test.ts`, the #7557
pin. `READERS` gains four app rows (`getMetaItem` active, plural,
`state: 'draft'`, `previewDrafts`) and two generated rows per type
`MetadataTypeSchema` declares except `object` (27 types today; the
schema does not list `package`): listing and by-name, so a new type gets
its rows with nothing to write. The seed registers one item of every
such type per package, as a code package registers it, plus a pending
app draft. New cases: absence equals the missing name on every type (no
`item`, no `registration`, and `NO_DRAFT` with the missing draft's exact
message); the control, another package's item of every type still listed
and served by name; the envelope is kept byte for byte apart from
`item`.
- `packages/runtime/src/domains/meta-disabled-package-doors.test.ts`
(new), through `HttpDispatcher.dispatch`: `/state/FIELD` and the
one-segment exit refuse `OBJECT_PACKAGE_DISABLED` and never ask
`registry.getObject` for the name; `/published` answers the refusal and
never asks `getPublished`; the service fallback's answer for a disabled
package's page is byte-identical to a missing name's; a control for
each.
- `packages/rest/src/meta-alternate-door-read-gates.test.ts`: a gated
dashboard and app the plain read withholds while their layers resolve,
driven with a caller the item's own gate refuses: `/history`, `/audit`,
`/diff` answer 404 `RESOURCE_NOT_FOUND` (403 `PERMISSION_DENIED` while
enabled), no secret in the body, and `historyMetaItem` / `auditMetaItem`
/ `diffMetaItem` never called. The existing "nothing behind the name"
case now says so on both reads (its layered double served the doc), so
it still pins the deleted item's answer: `/diff` absent, `/history` its
events.
- `packages/plugins/plugin-security/src/security-catalog-echo.test.ts`:
a disabled package's set echoes owned (by-name item withheld,
anti-vacuity), stays owned after the re-enable, and re-echoes owned; the
`stubProtocol` double now answers the envelope's `packageId` as the
protocol's `servedLockState` does.
- Pin sweep: `git grep` of `disablePackage(`, `/disable`,
`isPackageDisabled` and the `NO_DRAFT` sentence across every test: no
existing pin asserts a disabled package's non-object item is served by
name, so nothing flips; the suites of every package with a hit were run
(below).
## Verification (head `232339ab8`, after merging `origin/main` at
`b9ac35cbf`)
The head is now `ec7d6af8b`, one commit later. It changes only the
`BY_NAME_TYPES` filter line and its docblock in
`package-disable-enforcement.test.ts` (TS2367). At `ec7d6af8b`, the
typecheck scripts (`tsc` plus `check:test-typecheck`) of `objectql`,
`metadata-protocol`, `runtime`, `rest` and `plugin-security` each exit
0, and the pin is 15/15.
- Tests, full suites at this head: `@objectstack/metadata-protocol` 244
files / 20641 passed (3 skipped files); `@objectstack/objectql` 438 /
8249 passed; `@objectstack/runtime` 413 / 7095 passed;
`@objectstack/rest` 299 / 6284 passed (339 skipped);
`@objectstack/plugin-security` 188 / 3737 passed (45 skipped).
- Typecheck clean: `metadata-protocol`, `runtime`, `rest`,
`plugin-security`. Full `pnpm build` 76/76 after the merge.
- Gates: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (no paths) derived 73 commands at
`232339ab8`; all 73 exited 0 (the clone is unshallow now, so the two
shallow-clone refusals of the first round did not recur). `--ran` with
exit codes: "73 derived, 73 run, 0 NOT-MEASURED, 0 UNRUN".
`check-changeset-no-major` and `check-adr-0087-registration --base
origin/main`: exit 0.
- **Ablations**, one per changed tested behaviour, each through
`scripts/ablation-replace.mjs` with a `trap` restore proven by `blob ==
HEAD`:
- (a) `getMetaItem`'s check replaced by `|| true`, `pnpm --filter
@objectstack/metadata-protocol build`, `ablation-dist-preflight
--absent` passed (the suite resolves `dist/`). 31 by-name `READERS` rows
went red (the 27 generated rows, one per declared type but `object`, and
the 4 app rows), 0 listing rows; 3 failed / 12 passed. Restored and
rebuilt, marker back in `dist/index.js`: 15 / 15.
- (b)–(e) in `meta.ts` (the suite imports `src`): the `/state` check,
the one-segment check, the `/published` re-throw and the
service-fallback guard, each removed alone, each turned exactly its own
case red (1 failed / 6 passed); restored, 7 / 7.
- (f) `fetchCurrentMetaDocument`'s throw disabled: only the new
withheld-item case went red (1 failed / 426 passed); restored, 427 /
427.
- (g) `readServedPackageBinding` back on `item._packageId`: only the new
disabled-package echo case went red (1 failed / 18 passed); restored, 19
/ 19.
## Acceptance notes
- `origin/main` was merged into the branch (`8946a02d4`, a merge commit;
no rebase, no force-push), with no conflict.
- The member caller cannot reach the event and diff doors at all (their
capability gate); the bypass reaches an author who lacks the item's own
`requiredPermissions`, which is the gated caller measured above.
- No changeset for `@objectstack/objectql`: its diff is a test file,
outside its published `files`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Mr42C6J5TavuAMC2uqtFbR)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 5a9a80b commit 2bcd406
12 files changed
Lines changed: 606 additions & 20 deletions
File tree
- .changeset
- packages
- metadata-protocol/src
- objectql/src
- plugins/plugin-security/src
- rest/src
- runtime/src/domains
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
533 | 533 | | |
534 | 534 | | |
535 | 535 | | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
536 | 565 | | |
537 | 566 | | |
538 | 567 | | |
| |||
10375 | 10404 | | |
10376 | 10405 | | |
10377 | 10406 | | |
| 10407 | + | |
| 10408 | + | |
| 10409 | + | |
10378 | 10410 | | |
10379 | | - | |
10380 | | - | |
10381 | | - | |
| 10411 | + | |
10382 | 10412 | | |
10383 | 10413 | | |
10384 | 10414 | | |
| |||
11046 | 11076 | | |
11047 | 11077 | | |
11048 | 11078 | | |
| 11079 | + | |
| 11080 | + | |
| 11081 | + | |
| 11082 | + | |
| 11083 | + | |
| 11084 | + | |
| 11085 | + | |
| 11086 | + | |
| 11087 | + | |
| 11088 | + | |
| 11089 | + | |
| 11090 | + | |
| 11091 | + | |
11049 | 11092 | | |
11050 | 11093 | | |
11051 | 11094 | | |
| |||
11057 | 11100 | | |
11058 | 11101 | | |
11059 | 11102 | | |
11060 | | - | |
| 11103 | + | |
| 11104 | + | |
| 11105 | + | |
| 11106 | + | |
| 11107 | + | |
| 11108 | + | |
| 11109 | + | |
| 11110 | + | |
11061 | 11111 | | |
11062 | 11112 | | |
11063 | 11113 | | |
| |||
11292 | 11342 | | |
11293 | 11343 | | |
11294 | 11344 | | |
11295 | | - | |
11296 | | - | |
11297 | | - | |
11298 | | - | |
11299 | | - | |
11300 | | - | |
11301 | | - | |
11302 | | - | |
| 11345 | + | |
11303 | 11346 | | |
11304 | 11347 | | |
11305 | 11348 | | |
| |||
0 commit comments