Repository navigation
Commit dd1b803
Fixes #20116
Clause-②: no (narrowing)
**BREAKING** (an accept-set narrowing at the save doors; the changeset
carries the ADR-0087 disposition `registered
filter-comparand-types-and-widget-nested-slots-refused-at-save`).
Stage 2 of the save-door ↔ query-face parity collector: the two open
members of the seat's release `5857575995`. With the report half folded
in (seat answer `5859432781`, after PR #20238 landed as `6a6a17b6`),
both members are done, and so is the collector. The objectui producer
stage 1 found is carried by objectui#10790, not by this card.
- **M-type — done.** `FilterConditionSchema` now asks the comparand-TYPE
face (`normalizeFilterComparandTypes`) read-only, after the
comparand-shape face, inside the one judge stage 1 built
(`reportQueryFaceRefusals`,
`packages/spec/src/data/filter-save-door-refusals.ts`). A plain object
where a single value belongs, a `Map`, a class instance, a function, a
Symbol, `undefined` and a bigint beyond ±2^53 are refused on save — as
the comparand, an implicit-equality comparand or a list member — at
every reach the save doors have, and nothing the face passes is refused.
- **M-widget — done.** `DashboardWidgetSchema.filter`,
`ReportSchema.runtimeFilter` and `JoinedReportBlockSchema.runtimeFilter`
declare the analytics-carrier filter the two dataset carriers declare,
so each judges the slots INSIDE a nested relation the way the analytics
`where` door does. #20207's refinement was inline and module-private in
`dataset.zod.ts`; it moved verbatim into
`packages/spec/src/ui/analytics-carrier-filter.ts` (not in the `ui`
barrel), byte-neutral for `Dataset` (measured below). In `report.zod.ts`
only the two `runtimeFilter` lines (and the import) change.
## Zone 2, measured
### 1. Re-probe (base `origin/main` `17bd3187`; spec doors from `src`,
the analytics door from `service-analytics` `src` over a fresh spec
`dist`)
| member | FilterCondition | Dataset.filter | Measure.filter |
Widget.filter | Report.runtimeFilter | JoinedBlock.runtimeFilter | type
face (top) | analytics door |
|:--|:--|:--|:--|:--|:--|:--|:--|:--|
| `{ stage: { $eq: { a: 1 } } }` | ACCEPT | ACCEPT | ACCEPT | ACCEPT |
ACCEPT | ACCEPT | refuse 400 | refuse 400 |
| `{ stage: { $in: [{ a: 1 }] } }` | ACCEPT | ACCEPT | ACCEPT | ACCEPT |
ACCEPT | ACCEPT | refuse 400 | refuse 400 |
| `{ stage: { $eq: Map } }` | ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT
| ACCEPT | refuse 400 | refuse 400 |
| `{ stage: Map }` | ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT
| refuse 400 | refuse 400 |
| each of the four under `{ acct: … }` | ACCEPT | ACCEPT | ACCEPT |
ACCEPT | ACCEPT | ACCEPT | accept (not descended) | refuse 400 |
| `{ acct: { stage: { $in: ['won', null] } } }` | ACCEPT | refuse |
refuse | ACCEPT | ACCEPT | ACCEPT | accept | refuse 400 |
| `{ acct: { region: ['a'] } }`, `{ acct: { region: { $eq: ['a'] } } }`
(#20080) | ACCEPT | refuse | refuse | ACCEPT | ACCEPT | ACCEPT | accept
| refuse 400 |
| controls: `$gt: { $field }`, `$gt: Date`, `'{current_user_id}'`, `{
acct: { region: 'NA' } }` | ACCEPT | ACCEPT | ACCEPT | ACCEPT | ACCEPT |
ACCEPT | accept | accept |
After (src, same probe): every M-type cell is refused at the top level
on all six carriers, at the slot (`stage.$eq`) or the member
(`stage.$in.0`); every nested M-type and M-widget cell is refused on all
five analytics carriers (`widgets.0.filter.acct.stage.$in.1`,
`runtimeFilter.acct.stage.$in.1`,
`blocks.0.runtimeFilter.acct.stage.$in.1`); bare `FilterConditionSchema`
still accepts the nested cells (the face's reach); every control is
accepted.
### 2. One judge
The type face is asked inside `reportQueryFaceRefusals`, the function
both walks call, exactly where stage 1 asks the shape face — so it
reaches the shared walk (`checkFilterConditionComparands`) and the
analytics carriers' nested walk at once, with no second walk. The judge
raises ONE issue per slot, the first the query doors give in their order
(shape face → type face → flag rule; `parseFilterAST`, the engine seam
and `normalizeWhereComparands` all run them in that order). At the top
level and in the combinators, where a face refuses a slot, the shared
walk's own `#19514` `$icontains` and `#8793` preset arms stay silent on
that slot. Inside a nested relation on an analytics carrier they do not:
those two arms still judge nested slots beside the carrier walk's faces,
so a nested `$icontains` with a type-refused comparand, or a nested
one-bound `$between` of a preset name, carries two issues (the review
measured 303 such cells new at the head). No verdict moves either way;
the changeset and the entry say exactly this.
What the type face "judges only at request time" was measured: nothing.
The face is context-free (`context` is a message prefix only). The
request-time values are `{ $field }` references (the face steps around
them), `Date`s (accepted), and `{placeholder}` strings such as
`{current_user_id}` / `{today}` — strings at save time, resolved by
`resolveWhereTokens` only AFTER both faces have run on the engine seam.
Each is pinned accepted-and-kept (§4 controls), plus a bigint within
2^53, which the save door keeps as written (the face would narrow it on
a query).
Two classifications follow the type face rather than restating it: a
field value is a comparand unless it is a PLAIN object (prototype
`Object.prototype` / `null`), so `{ stage: new Map() }` reaches the face
instead of being walked as an empty nested relation; and the whole field
entry is shown to the face first, so a spec it classifies as a `{ $field
}` reference is stepped around whole, as the face does (pinned as a
control).
### 3. The dashboard and report carriers — extraction, byte-neutral
- The move is its own commit (`dfa424f6`), verbatim; `DatasetSchema` /
`DatasetMeasureSchema` call `analyticsCarrierFilter()` as before.
- `z.toJSONSchema` of `DatasetSchema`, `DatasetMeasureSchema`,
`DashboardSchema` and `DashboardWidgetSchema`: sha256
`246850f2b9efdf1b…` at base `17bd3187`, at the extraction commit, and
after the widget carrier — byte-identical. With `ReportSchema` and
`JoinedReportBlockSchema` added, the six projections hash
`35ba34f964bb8fd6…` both before the report fold (`6a0cfb05`) and after
it. The parse probe is identical base vs extraction.
- `dropped-refinements.baseline.json`: the `ui/Dataset` and
`ui/DatasetMeasure` rows are unchanged. The merge of `6a6a17b6`
conflicted only in this ledger's measured header; main's side was taken
and the branch's sites re-added from build-schemas' own printed
"corrected entries" (no hand-picked site): `ui/DashboardWidget`
`filter`, `ui/Dashboard` `widgets.element.filter`, `ui/Report`
`runtimeFilter` and `blocks.element.runtimeFilter`,
`ui/JoinedReportBlock` `runtimeFilter`, and the same three positions in
the four installed-package envelopes — +17 sites, 0 removed,
`measured.droppedRefinementSites` 605 → 622.
- Pin: §7 of the parity test asserts the carrier projects to exactly
`FilterConditionSchema.optional()`'s JSON Schema, and that the widget
filter and the report `runtimeFilter` keep their published descriptions.
### 4. The enumerating pin
`filter-save-door-face-parity.test.ts`, extended, not duplicated:
- §1 `queryFacesRefuse` now asks all three rules. The operator arms
still derive from `FieldOperatorsSchema`'s keys, and a new assertion
requires the type face to judge EVERY declared operator over the battery
(its own test reconciles its scalar/list split against the same
vocabulary). The battery gained the type face's shapes and neighbours
(Map, class instance, function, Symbol, `{ $field: 5 }`, `{}`, bigints
within and beyond 2^53, lists holding a plain object / Map / `undefined`
/ big bigint, a plain-object `$between` bound, bigint pairs).
- §5 runs the operator × comparand table and the implicit slot, one and
two hops down, on every analytics carrier — dataset `filter`, measure
`filter`, dashboard widget `filter`, report `runtimeFilter`, joined
block `runtimeFilter` (the two report rows were `EXPECTED_OPEN` until
the fold and now sit in `CARRIERS`), plus a pin that the carrier list is
exactly those five.
- §6 walks the type face's own conformance table
(`FILTER_COMPARAND_TYPE_CASES`): every `door-refusal` row is refused on
save; every `matches` / `compiles` row is accepted AND kept as written.
### 5. Producer census (narrowing), with lit controls — 0 hits
| corpus | object in a scalar slot | object list member | bigint literal
| `undefined` under an operator | `new X` under an operator | nested
relation holding a list / operator map in a filter |
|:--|:--|:--|:--|:--|:--|:--|
| objectstack `examples/**` @ `eaf7a925` | 0 | 0 (control: `$in` lists
3) | 0 | 0 | 0 | 0 (control: filters with an operator-map first entry
14) |
| objectstack non-test `packages/**` @ `eaf7a925` | 25 raw, all prose /
driver `case` labels / the type face's own table (control: `$field` in a
scalar slot 67) | 26 raw, all prose / `$field` members / `{placeholder}`
strings (control 238) | 3, prose | 31, prose (control: `null` 83) | 6,
`new Date` / the table | 0 (control 162) |
| objectui @ pin `f8a9d0fb05` | 0 (control 1) | 0 (control 12) | 0 | 1,
a comment | 1, a refusal message | 0 (control 13) |
| cloud `main` @ `96eb092fbf` | 0 (control 2) | 1, a comment (control
11) | 0 | 0 | 0 | 0 (control 14) |
Plus a runtime walk of every value under a `filter` / `runtimeFilter` /
`where` / `having` / `relatedListFilter` key in the loaded example
stacks, old door vs new door on each: `app-crm` 8, `app-todo` 15,
`app-multi-package` 0, `app-showcase` 20 (its metadata modules; its
config needs connector builds) — 0 refused by the new door alone. Lit
control: a planted `{ stage: { $eq: { a: 1 } } }` and a planted nested
`$in` null member fire the detector in every run. No ADR-0087 D2
conversion: nothing to convert.
## The words (changed or new refusal text)
A type-face cell reads the face's own sentence less its ` at where.SLOT`
clause — nothing restated:
> `Filter comparand is a plain object ({"a":1}), which no driver can
compare. A comparison value must be a string, number, bigint, boolean,
null or Date. Refusing rather than guessing: the backends disagreed on
this input (crash / zero rows / silently edited query). The filter was
NOT applied, and an unapplied filter would have returned the UNFILTERED
result set.`
at `filter.stage.$eq`, or at the member (`filter.stage.$in.1`).
`undefined` gets the face's own sentence (`Filter comparand is
undefined. { key: undefined } cannot be told apart from an omitted key,
… Write the null predicate — {"$eq": null} / {"$ne": null} — or omit the
key. …`), a bigint beyond 2^53 its (`Filter comparand is the bigint …n,
whose magnitude exceeds 2^53 — …`). The clause removed is the one the
face was handed (`where` plus this slot), so nothing is parsed out of
the text; if the face ever spells its location differently, the whole
message is reported location included, and §2's "no ` at where.`" pin
goes red.
Nested cells on the widget and report carriers print the same sentence
as their top-level form (stage 1 and #20207's rule): e.g.
`widgets.0.filter.acct.stage.$in.1` and `runtimeFilter.acct.stage.$in.1`
carry the enforced `$in` slot's null-member sentence.
## Behaviour changes, each pinned
| change | pin |
|:--|:--|
| type-face cells refused on save, top level + combinators, every
`FilterCondition` carrier | parity §1 (all positions), §3 (six
carriers), §6 |
| type-face cells + stage-1 cells + #20080 lists refused INSIDE a
relation on the widget filter and both report `runtimeFilter`s | parity
§5 (all five carriers) |
| one issue per slot at the top level and in the combinators, shape →
type → flag; the `$icontains` / preset arms silent on a face-refused
slot there (not inside a relation on an analytics carrier) — a dedupe,
no verdict moves; stated at that reach in the changeset | parity §6 "one
slot, one issue" (top level) |
| `POST /analytics/dataset/query` `selection.runtimeFilter` and `POST
/analytics/query` `where` with `{ stage: { $eq: { a: 1 } } }` / `{
stage: { $in: ['won', { a: 1 }] } }`: `400 INVALID_FILTER` → `400
VALIDATION_FAILED` located on the member |
`packages/rest/src/analytics-filter-refusal-envelope.test.ts`
`AT_THE_DOOR` rows + the sibling-schema control |
| request-time values accepted and kept | parity §4 |
## Pin sweep
① Every refusal code and message this touches was grepped repo-wide. The
type face's text is unchanged (only called). The HTTP-door code move has
two routes and both are pinned in the rest file above; no rest / runtime
test sent a type-face cell through a schema door before (grep over
`packages/**` tests outside spec: the plain-object / Map / `undefined` /
bigint comparand hits live in the analytics door's, the drivers',
objectql's engine and read-scope suites, which call the faces directly,
not a schema). Spec pins whose words could move — a flag with an object
/ `undefined` comparand, `$icontains` with a type-refused comparand, a
preset endpoint on a malformed `$between` — have no existing pin. ② The
flipped rest rows assert the substance: status 400, `VALIDATION_FAILED`,
exactly one `details.fields[]` entry at the member, the sentence, and no
` at where.`.
## Tests
Final head `830a071a` (merges `origin/main` `dfd8e398`, then corrects
the dedupe sentence of the changeset and the semantic entry — text
only), everything through `scripts/pm/os-verify-lock.sh`, `VERDICT
command-exit 0`:
- `@objectstack/spec` at `830a071a`: build 0; `check:generated` 0 ("All
15 generated artifacts are up to date"); `typecheck` 0; full suite 587
files / 17031 passed / 1 todo; the parity pin alone 153 passed.
`check-adr-0087-registration` 0 (`[BREAKING+bang+clause-②-narrowing]
registered
filter-comparand-types-and-widget-nested-slots-refused-at-save`) and
`check-changeset-no-major` 0 at `830a071a`.
- At `e9f93902` (review head): spec full suite 585 files / 16988 passed
/ 1 todo.
- At `3d9621a8` (the fold plus the ledger, before the second `main`
merge, which touches no file of this diff's behaviour): spec 585 / 16955
passed; consumer closures built (exit 0); `service-analytics` 129 files
/ 3041 passed; `lint` 111 / 4297; `rest` (`--project local`) 202 / 3664
passed / 1 skipped. `rest` typecheck 0 at `54b99f3c` (the rest file is
unchanged since). The two new rest rows ran by name (`a plain object
where a single value belongs → 400 VALIDATION_FAILED, located on the
member`, `a plain object as an $in member → …`).
- Ablation, through `scripts/ablation-replace.mjs` (WRAP), each anchor
x1 → x0 on disk with the blob changed, each restore proven blob == HEAD
blob and `git diff HEAD` empty (script trap restores on EXIT/INT/TERM).
Parity pin:
- at `3d9621a8`, 153 tests — **report-carrier arm**:
`ReportSchema.runtimeFilter` carrier stripped
(`analyticsCarrierFilter().unwrap().optional()`) → 18 failed;
`JoinedReportBlockSchema.runtimeFilter` stripped → 18 failed; restored →
153 passed (`unwrap` markers on disk after restore: 0);
- at `54b99f3c`, 152 tests — type face off → 48 failed; widget carrier
off → 18; shared walk's Map / class-instance classification off → 6;
nested walk's classification off → 3; one-issue-per-slot `continue` off
→ 1 (direction: MORE diagnostics — two issues at `name.$icontains`);
restored → 152 passed.
- No leg needs a build: the pin imports spec `src`.
- Gates at `830a071a`: `dispatch-gates --commands` derived 90 on the
actual paths (the 89 of the review head plus `node
scripts/check-issue-citations.mjs`, which main's `7338efe0` now runs
locally); 88 exit 0 — `check:doc-formula-expressions` and
`check:lean-entry-closure` first answered exit 3 (their `formula` /
`objectql` builds were absent in the re-created worktree) and exit 0
after that closure was built under the lock;
`check:dual-build-cjs-loads` and `check:type-check-debt` exit 3
(PREREQUISITE NOT MET: whole-repo build) = NOT MEASURED; `--ran` with
recorded codes: 90 accounted, 88 run, 2 NOT MEASURED.
- ESLint, narrowed and proven, at `830a071a`: `eslint --no-inline-config
--format json` over the 10 changed `.ts` files (count read from the
JSON) → 0 errors, 0 warnings. The population is `eslint.config.mjs`,
which "never enables type-aware linting (no `parserOptions.project`, no
typed `@typescript-eslint` rules) for ANY file", so the diff cannot move
a verdict on an untouched file. Repo-wide `pnpm lint` is CI's.
- `main` moved after `830a071a` too (2 commits at the gate derivation,
none touching what this answer derives from, as `dispatch-gates` read
it).
- NOT MEASURED: `objectql`, `metadata-protocol`, `runtime` and example
suites (no fixture carries a refused shape through a schema door by the
sweep above; CI runs them).
## Hand-written docs the drift check named (github-actions comment
`5860213966`)
Each re-read against this PR's behaviour — the type-face refusals at
save, the widget / report `runtimeFilter` nested-relation refusals, and
the analytics routes' code move for the type face's JSON cells:
| page | verdict | why |
|:--|:--|:--|
| `content/docs/api/data-api.mdx` | unchanged | Its `INVALID_FILTER`
sentences are about the data routes' `?filter` (the engine's normalizer,
not a `FilterConditionSchema` parse), and the `/analytics/query` section
says only that `where` is the `FilterCondition` `find()` accepts — still
true; no sentence names the code a type-face cell gets. |
| `content/docs/api/error-catalog.mdx` | unchanged | `INVALID_FILTER` /
`VALIDATION_FAILED` are defined generically; no sentence claims the
analytics routes answer `INVALID_FILTER` for a plain-object comparand. |
| `content/docs/data-modeling/analytics.mdx` | unchanged | The widget
`filter` and report `runtimeFilter` examples (`$nin` list,
`{current_quarter_start}` placeholder) are accepted by the new doors;
the placeholder paragraph and the "one author-facing shape" section stay
true; nothing says a nested-relation filter saves on those carriers. |
| `content/docs/protocol/objectql/query-syntax.mdx` | unchanged | Its
`FilterConditionSchema` / field-reference / relation-traversal text
stays true: `{ $field: 'col' }` is still accepted, and the page makes no
claim about a plain-object, `Map` or `undefined` comparand passing
validation. |
No release page (`content/docs/releases/**`) names these shapes; none
was touched.
## Acceptance notes
- **Request doors keep two codes for one family.**
`DatasetSelectionSchema.runtimeFilter` /
`AnalyticsQueryRequestSchema.where` carry the shared reach, so a
top-level refused slot answers `VALIDATION_FAILED` at the schema door
while the same slot inside a relation answers `INVALID_FILTER` from the
analytics normalizer. Both 400, both located; not a save door, so not
this collector's. Noted, not filed (carrier: none).
- **File surface**, as amended by the seat (`5859432781`):
`report.zod.ts` (the two `runtimeFilter` carriers only); and, accepted
as the order's own mechanism, `data/filter-save-door-refusals.ts` (stage
1's judge), `ui/analytics-carrier-filter.ts` + `ui/dataset.zod.ts` (Zone
2.3's extraction),
`packages/rest/src/analytics-filter-refusal-envelope.test.ts` (the
HTTP-door pin).
- `GlobalFilterOptionsFromSchema.filter` (a dashboard's options source)
is an engine query, not charted through the analytics door, so it keeps
the shared reach.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Rjy9MeetSfq34PKn81CRiN)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 826f327 commit dd1b803
12 files changed
Lines changed: 1034 additions & 252 deletions
File tree
- .changeset
- packages
- rest/src
- spec
- src
- data
- migrations
- entries/semantic
- ui
| 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 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
220 | 220 | | |
221 | 221 | | |
222 | 222 | | |
223 | | - | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
224 | 227 | | |
225 | 228 | | |
226 | 229 | | |
| |||
236 | 239 | | |
237 | 240 | | |
238 | 241 | | |
| 242 | + | |
| 243 | + | |
239 | 244 | | |
240 | 245 | | |
241 | 246 | | |
| |||
270 | 275 | | |
271 | 276 | | |
272 | 277 | | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
273 | 295 | | |
274 | 296 | | |
275 | 297 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| |||
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
| 58 | + | |
58 | 59 | | |
59 | 60 | | |
60 | 61 | | |
| |||
79 | 80 | | |
80 | 81 | | |
81 | 82 | | |
| 83 | + | |
| 84 | + | |
82 | 85 | | |
83 | 86 | | |
84 | 87 | | |
| |||
156 | 159 | | |
157 | 160 | | |
158 | 161 | | |
| 162 | + | |
159 | 163 | | |
160 | 164 | | |
161 | 165 | | |
| |||
179 | 183 | | |
180 | 184 | | |
181 | 185 | | |
| 186 | + | |
| 187 | + | |
182 | 188 | | |
183 | 189 | | |
184 | 190 | | |
| |||
241 | 247 | | |
242 | 248 | | |
243 | 249 | | |
| 250 | + | |
244 | 251 | | |
245 | 252 | | |
246 | 253 | | |
| |||
264 | 271 | | |
265 | 272 | | |
266 | 273 | | |
| 274 | + | |
| 275 | + | |
267 | 276 | | |
268 | 277 | | |
269 | 278 | | |
| |||
287 | 296 | | |
288 | 297 | | |
289 | 298 | | |
| 299 | + | |
290 | 300 | | |
291 | 301 | | |
292 | 302 | | |
| |||
309 | 319 | | |
310 | 320 | | |
311 | 321 | | |
| 322 | + | |
| 323 | + | |
312 | 324 | | |
313 | 325 | | |
314 | 326 | | |
| |||
1167 | 1179 | | |
1168 | 1180 | | |
1169 | 1181 | | |
| 1182 | + | |
1170 | 1183 | | |
1171 | 1184 | | |
1172 | 1185 | | |
1173 | 1186 | | |
1174 | 1187 | | |
1175 | 1188 | | |
| 1189 | + | |
1176 | 1190 | | |
1177 | 1191 | | |
1178 | 1192 | | |
| |||
1276 | 1290 | | |
1277 | 1291 | | |
1278 | 1292 | | |
| 1293 | + | |
1279 | 1294 | | |
1280 | 1295 | | |
1281 | 1296 | | |
| |||
1396 | 1411 | | |
1397 | 1412 | | |
1398 | 1413 | | |
| 1414 | + | |
| 1415 | + | |
1399 | 1416 | | |
1400 | 1417 | | |
1401 | 1418 | | |
| |||
0 commit comments