Repository navigation
Commit 6106b73
feat(spec,security): a controlled_by_parent object may declare an optional master (#30940)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DC6kPDNUbz8zYdVCQqG8k42)_
domain:server#1 · os-elon-musk · session_016WV4cxZsyeZedvM4Ea8Ahs
Fixes #30905
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: follow-up owed — #30907 (card 3/3 of the ruling: the docs
and skills that teach `controlled_by_parent` still say the master is
always required); this diff teaches the optional master in ADR-0055 §1
and §2 as amended, in `os explain relationship/master-detail-required`
and `os explain security-controlled-by-parent-no-relation`, and in the
semantic migration entry `cbp-optional-master-admitted`
Claim: 6685806956 (the seat's latest full re-post of 6682716818: this
branch, the amended file surface, and five `ui/` test files as a surface
conditional on ruling A; supersedes 6684936456, 6683296158 and
6682753060) · Ruling: A, record 6687830530 · Branch:
`claude/project-thread-prdvgm`
## 维护者速读
- 改了什么:`controlled_by_parent` 的子对象现在可以把它唯一的 `master_detail` 字段写成
`required:
false`(可选主表)。挂了主记录的行照旧由主记录决定谁能读;主表引用为空的行,只有它的属主(`owner_id`)和对该子对象有「查看全部」的人能读。另含
lint 规则、explain 叙述、ADR-0055 §1/§2 修订、升级清单的相应调整,以及 ui 里 5 个测试按裁决 A
改的对照断言(只改测试,Studio 源码不动)。
- 为什么改:「销售看自己客户下的联系人;联系人可以不挂客户」以前没有声明式写法,只能复制一个属主字段再用钩子同步。这是您对 #30905
的裁决 A(三张卡的第 1 张)。
- 风险与回滚:读侧只在唯一的读收口 `computeControlledByParentFilter` 里多了一个 `$or`
分支,仍在租户墙之下;必填主表的过滤条件逐字节不变。写侧不变:主表引用为空的写入仍然 422(第 2 张卡 #30906 才改写闸)。回滚即撤
PR,没有数据迁移。
-
先看:`packages/plugins/plugin-security/src/security-plugin-cbp-read-filter.ts`
的新分支(约 30 行),再看新测试 `controlled-by-parent-optional-master.test.ts`
的读侧几条规则。
- 两分钟验证:`pnpm --filter @objectstack/plugin-security exec vitest run
src/controlled-by-parent-optional-master.test.ts`,预期 24/24 通过;把 owner
分支删掉再跑会红 6 条(见下文消融)。
- 需要你做的:A/B/C 已裁 A(记录 6687830530),本 PR 按 A 实现,ui 的 5 个测试已随之改好。剩下的合同复审和
ADR 的 Tier H 批准由席位安排;Studio 不显示可选主表这个缺口,席位另开 ui 卡。
## What changes
**Spec (`@objectstack/spec`, `minor`).** A `controlled_by_parent` object
whose only `master_detail` is declared `required: false`, and which
declares no required one, now has an **optional master**.
- `controlledByParentMasterOf(fields)` is new in
`packages/spec/src/data/controlled-by-parent-master.ts`, exported from
`@objectstack/spec/data`: it answers the master field and whether it is
optional (one required `master_detail` ⇒ that, not optional; more than
one required ⇒ none; otherwise exactly one `master_detail` declared
`required: false` ⇒ that, optional). `controlledByParentMasterRefusal`
(the parse's one-master rule) judges through it.
`isControlledByParentMasterField` still means the REQUIRED master.
- `ObjectSchema.create()` (`forceCbpMasterDetailRequired` in
`object.zod.ts`): an explicit `required: false` on the object's ONLY
`master_detail` is kept as written; beside another `master_detail` it is
still refused, with a message saying why. The force half (omitted
`required` ⇒ `true`) is unchanged, and so is the D2 conversion
`cbp-master-detail-required-defaulted` (its test file only gains rows
that pin it).
- Every other refusal stands: two masters, a required `lookup` only, a
`readonly` / `system` master (required or optional).
- `conversions/refused-shapes.ts`: the `master-not-required` arm no
longer lists an explicit `required: false` sole master. A D3 semantic
entry, `migrations/entries/semantic/18.cbp-optional-master-admitted.ts`
(registered in `registry.ts.template`, order 119), records the widening.
- `readable-ids-conformance.ts` gains a case for the leaf shape this
produces: a leaf whose scope ORs a nested leaf, OR-ed with an
empty-reference arm.
- `api-surface/data.json` and `export-origins/data.json` regenerated
(`controlledByParentMasterOf`).
**Read (`@objectstack/plugin-security`, `minor`).**
`computeControlledByParentFilter` (`security-plugin-cbp-read-filter.ts`)
is still the one read chokepoint. For a required master it returns the
leaf it always did. For an optional master it returns
```
{ $or: [ { [fk]: { $in: ReadableIds.of(master) } }, nullMasterArm ] }
```
where `nullMasterArm` is `{ [fk]: null }` for a holder of View All on
the detail (`permissionEvaluator.hasSuperuserReadBypass(object,
permissionSets)`), else `{ $and: [ { [fk]: null }, { owner_id: caller }
] }` when the detail carries `owner_id`, else no arm (master arm only).
The `$or` is this layer's answer, AND-ed by every caller with the tenant
wall and the detail's own layers as before.
- The relation resolver (`security-plugin-cbp-relation.ts`) picks the
optional master through `controlledByParentMasterOf` when there is no
required candidate, and `CbpRelation` carries `optional` and
`ownerColumn` (`security-plugin.ts`). `reportCbpMasterUnresolved`'s
advice names both spellings.
- The five call sites pass the permission sets they already resolved:
the authz middleware for the caller and, on a delegated request, for the
delegator; `getReadFilter`; and the delegated read filter for the
delegate and the delegator. So View All is read for the principal each
answer is for.
- `explain-engine.ts`: `security/explain` on a record read names the
null-master arm (which arm returned the row; for a withheld row with an
empty reference, who reads it instead, and that a queue row is View
All's alone).
**Lint (`@objectstack/lint`, `minor`).**
`relationship/master-detail-required` passes the optional master, still
reports `readonly` / `system` on it, and reports an omitted `required`
on the only `master_detail` at `error` naming both spellings.
`validate-security-posture.ts` resolves the optional master as the parse
does, so `security-controlled-by-parent-no-relation` no longer calls a
valid optional master "no relation". `rule-explanations.ts` teaches it.
**ObjectQL (`@objectstack/objectql`, `minor`).**
`engine-readable-ids-permission-formula.ts` resolves the master through
`controlledByParentMasterOf`, so the boot refusal of a master the engine
cannot compose a read over judges an optional master's detail too.
**Record (Tier H).**
`docs/adr/0055-master-detail-controlled-by-parent.md`: status line,
inline notes at §1 and §2, and a new section "Amendment (2026-11-06,
#30905): an optional master, and the null-master row its owner reads".
**Ledger.** `scripts/engine-double-contract.pinned.json` records the new
suite's one pinned `findOne` double (`check-engine-double-contract
--write`, no seam lost).
## Why
Card #30905, maintainer ruling A: "a rep sees the contacts of the
accounts they own; a contact need not have an account" had no
declarative form. `controlled_by_parent` refused a `master_detail` that
is not required at the builder and at the parse, a `lookup` derives no
access, and the only route was a copied owner field kept in step by
hooks. Premise re-read on `main`: both refusals are at the lines the
card cites, `object.zod.ts` 4406-4417 (the builder's explicit-false
throw) and 2743-2752 (the parse refinement), no drift. One gap the card
did not name is the open question below.
## Tests
New suite
`packages/plugins/plugin-security/src/controlled-by-parent-optional-master.test.ts`
(24 tests, both read faces: the data middleware and `getReadFilter`):
- the injected leaf shape (master arm unchanged, OR-ed with `fk IS NULL
AND owner_id = caller`), and a required master's leaf unchanged (no
`$or`);
- the rep reads the rows of masters they read and the null-master rows
they own, nothing else;
- a row that NAMES a master is never read through the owner arm (the rep
owns a contact under an account they cannot read);
- a caller who reads another master does not see the null-master rows
they do not own;
- a null-master row with no owner (an ADR-0142 queue row) is not read
through the owner arm;
- View All on the detail reads every null-master row, the queue row
included, and does NOT open the master arm;
- an object with no `owner_id` column: a null-master row is View All's
alone;
- chain: the children of an intermediate row with no master are read by
that row's owner, exactly those;
- the answer is the disjoint union of the two arms;
- the write gate is unchanged: an empty master reference on insert is
still 422 `MISSING_REQUIRED_FIELD`; an insert naming a readable master
reaches the executor;
- the resolver resolves the optional master (no "resolves no master
relation" report), and the parse accepts the detail whole;
- explain: a returned null-master row names the empty reference and the
owner arm; a withheld one names who reads it, and a queue row is View
All's alone; a row that names its master, and every row of a required
master, keep the master-scope narration.
Updated pins: spec (`controlled-by-parent-master.test.ts`,
`object.test.ts`, `cbp-master-detail-required-defaulted.test.ts`,
`refused-shapes.test.ts`, `stack-cbp-extension-master.test.ts` (an
extension making the one master optional passes like its declared
control; beside a second master it is refused), and
`shared/retired-key-removal-version.pin.test.ts` (the refusal the parse
still answers on the defaulted conversion's surface is now two
`master_detail` fields with no `required`)), lint
(`data-model-rules.master-detail-required.test.ts`,
`validate-security-posture.test.ts`,
`validate-security-posture-extension-merge.test.ts`),
`controlled-by-parent-sharing.test.ts` (the retired-tier table's omitted
row now parses, pinned as the parse/builder divergence below),
`packages/metadata-protocol/src/protocol.stored-migration.test.ts` (the
stored `required: false` row is counted canonical, no longer listed),
and five `ui/packages/app-shell` tests that used the spec as an oracle
(below, under Verification). The readable-ids conformance suites passed
on the memory, sql, sqlite-wasm, mongodb and turso drivers; the new case
was confirmed by name on memory, sql and turso (local and remote).
**Ablation** (`node scripts/ablation-replace.mjs`, each restore verified
blob equal to HEAD, `2ba0466291de` for the read filter):
- (a) the null-master arm dropped (`nullMasterArm` replaced with
`null`): **6 red** — the injected-shape pin, "the rep reads ... the
null-master rows they own" on both faces, the chain case on both faces,
and the disjoint-union case.
- (b) the owner arm unconfined (`{ owner_id: caller }` without the `fk
IS NULL` conjunct): **8 red** — the six above plus "a row that NAMES a
master is never read through the owner arm" on both faces.
## Acceptance notes
- **A parse cannot tell an omitted `required` from `required: false`**
(the parsed field map defaults it to `false`). So under option A a raw
parse, and `defineStack` over a plain object literal (it returns the
parsed stack), admits an omitted `required` on the only `master_detail`
as the optional master. The builder still forces it to `true`, the D2
conversion still converts a stored row, and `os lint` reports it at
`error`. The runtime registry reads `required === false`, so an object
that reaches it unparsed with `required` omitted still resolves no
master and every read is denied. Pinned in
`controlled-by-parent-master.test.ts`, `object.test.ts`,
`validate-security-posture.test.ts` and
`controlled-by-parent-sharing.test.ts`.
- An extension that redeclares the master `required: false` makes it the
optional master (`validate-security-posture-extension-merge.test.ts`).
- A cycle or depth refusal of the chain still denies the whole object,
null-master rows included.
- The file surface grew beyond the first claim by the files the seat
accepted in 6683296158: `validate-security-posture.ts`,
`rule-explanations.ts`, `engine-readable-ids-permission-formula.ts`, a
docblock in `engine.ts`, and the plumbing in `security-plugin.ts`,
`security-plugin-authz-middleware.ts`, `security-plugin-read-filter.ts`.
Declared to the seat after that:
`packages/metadata-protocol/src/protocol.stored-migration.test.ts` (a
test pin this change moves) and one docblock in
`packages/objectql/src/engine-top-level.ts`
(`UnservableControlledByParentMaster.field`, which the change made
false). Generated or ledger files: `api-surface/data.json`,
`export-origins/data.json`,
`scripts/engine-double-contract.pinned.json`. On ruling A, the
conditional surface of claim 6685806956: five `ui/packages/app-shell`
test files, test-only.
- `forceCbpMasterDetailRequired` is on the claim's ⛔ list for its force
half; this diff changes only its explicit-false throw (the
`master-not-required` refusal at `ObjectSchema.create()` the file
surface names), narrowed to the object's only `master_detail` as the
seat agreed. The force half and the D2 conversion source are unchanged.
- Out of scope and untouched: the write gate (#30906), the docs and
skills (#30907), `ui/**` beyond those five test files (Studio source
included), `.claude/**`.
- Studio still writes `required: true` on a cbp master and its OWD
overview names a master only for `required: true` (`owd-sharing.ts`
`deriveMasterObject`), so it shows none for an optional master. The seat
cards that display gap separately; this PR does not change it.
## Ruling (maintainer, decision card)
`FieldSchema.required` is `z.boolean().default(false)` and the parse's
cbp refinement reads the PARSED field map, so the parse cannot tell an
explicit `required: false` from an omitted one. The card's "accept
explicit false at the parse" and "omitted still means required" could
not both hold at the doors that parse without the builder force or the
D2 conversion (`defineStack` over a plain object literal, `os validate`
/ `os build` on such a config, the metadata save door). Three options
went to the maintainer; **A was ruled** (record 6687830530):
- **A** (implemented): the parse accepts the one `master_detail` that is
not `required: true`, so at those raw doors an omitted `required` is an
optional master, not a refusal. Omitted stays required where the
omission is visible: `ObjectSchema.create()` (force unchanged), stored
rows (D2 unchanged), Studio (writes `required: true`). `os lint` reports
the omission at `error`. The runtime reads `required === false`, so an
object that reaches it unparsed with no `required` key still resolves no
master (fail closed). Consequence: a `defineStack` plain literal that
omits `required` now passes and runs as an optional master.
- B (not chosen): refuse the omission at every parse door through a new
raw-input mechanism on `ObjectSchema`.
- C (not chosen): a separate explicit opt-in key instead of `required:
false`.
## Verification
Head `1899fc5e98`: the branch's four earlier commits (on `main`
`98f640d5`), a plain merge of `main` `1b3e22f0` (`fd6d188b`, one file in
common, `packages/spec/src/data/object.zod.ts`, merged cleanly), the ui
test commit `469499edc7`, the contract-review fix commit `fa5c3fee34`
(the ADR-0055 invariant in five `scripts/adr-anchors` files moved to §1
as amended; ADR-0055's amendment cites ruling record 6687830530 instead
of an open question), a plain merge of `main` `000a4c0d` (`c6541998`, no
file in common), and `1899fc5e98`, test-only: module-top `import
'@objectstack/lint'` in `protocol.stored-migration.test.ts`, and `import
'@objectstack/lint'` + `import '@objectstack/formula'` in
`ObjectFieldInspector.cbpRequiredMaster-12326.test.tsx`, for the
test-source alias gate's source-reach half (the sources keep their
dynamic imports). The tables below were measured on `2374556f91` /
`835c94d1` after a whole-tree `turbo run build
--filter=!@objectstack/docs` (118/118 tasks, lock VERDICT command-exit
0); the delta since is listed after them.
Each run under the verify lock, VERDICT command-exit 0 for the lock,
exit code per command recorded:
| Package | Command | Result |
|---|---|---|
| spec, lint, objectql, plugin-security | `pnpm --filter
@objectstack/NAME typecheck` (tsc plus `check:test-typecheck`) | exit 0
each |
| `@objectstack/spec` | `test` (local project) | 817 files passed, 1
skipped; 25856 tests passed, 1 skipped, 1 todo (the skip is
`scripts/root-entry-type-nameability.pin.test.ts`'s dist-freshness gate:
spec test files changed after the build) |
| `@objectstack/lint` | `test` | 196 / 196 files; 8288 / 8288 tests |
| `@objectstack/plugin-security` | `test` | 247 / 247 files; 5710
passed, 45 skipped |
| `@objectstack/objectql` | `test` (local project) | 558 / 558 files;
11160 / 11160 tests |
| `@objectstack/metadata-protocol` | `test` | 320 files passed, 4
skipped; 22487 passed, 26 skipped |
| `@objectstack/verify` | `test` | 48 / 48 files; 408 / 408 tests |
| `@objectstack/plugin-sharing` | `test` | 67 / 67 files; 1436 / 1436
tests |
| `@objectstack/service-analytics` | `test` | 211 / 211 files; 5015
passed, 300 skipped |
| cli, plugin-audit, service-storage, rest | the 13 test files there
that name `controlled_by_parent` | 13 / 13 files; 347 / 347 tests |
The spec, lint, plugin-security and objectql full runs were taken on
`835c94d1` (the source as here; the commit after it changes three test
files and one docblock); the spec suite was then re-run whole on
`2374556f91` (above), as were the rest.
**Test UI under ruling A.** On `2374556f91`, Test UI was red in shards
1, 2, 7 and 16: six cases in five `ui/packages/app-shell` test files
that used the spec's `ObjectSchema` as an oracle for "a
`controlled_by_parent` object whose only `master_detail` omits
`required` is refused". Under A the parse admits it as the optional
master. Re-pointed test-only in `469499edc7` (Studio source unchanged):
- four control legs now assert that shape parses with `required: false`
before Studio writes `required: true`, and with `required: true` after:
`metadata-admin/previews/object-fields-io.test.ts`,
`metadata-admin/inspectors/ObjectFieldInspector.cbpRequiredMaster-12326.test.tsx`,
`studio-design/ObjectSettingsPanel.cbpRequiredMaster-12326.test.tsx`,
and the save case of
`studio-design/PackageOwdOverviewPanel.cbpRequiredMaster-12326.test.tsx`;
- two display-agreement rows ("one master without required", in
`studio-design/owd-sharing.test.ts` and in the shapes table of
`PackageOwdOverviewPanel.cbpRequiredMaster-12326.test.tsx`) now record
that the parse admits the shape while Studio names no master, as the
runtime does for an unparsed object.
Run from `ui/` on this head: `pnpm exec vitest run` on the five files, 5
/ 5 files, 77 / 77 tests; `tsc -p tsconfig.test.json --noEmit` in
`ui/packages/app-shell`, lock VERDICT command-exit 0, 0 errors. The
whole Test UI matrix is read from this PR's check runs.
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived the families; each ran sequentially
with its exit code recorded before any pipe. Four needed a second run:
`check:dual-build-cjs-loads` and `check:i18n` exit 3 (no dist) before
the whole-tree build and exit 0 after; `check-plugin-teardown-shape
--self-test` exit 3 (shallow clone, pinned fixture commit unreadable)
and exit 0 after fetching that commit; `check:engine-double-contract`
exit 1 (the new suite's double not in the ledger) and exit 0 after
`--write`. `pnpm lint`: exit 0.
- `dispatch-gates --ran` on `2374556f91`: "108 derived famil(ies)
accounted for — 108 run, 0 NOT-MEASURED".
- Delta on `469499edc7`: the derivation grew to 112 families
(`check-sdui-manifest` and its self-test, `pnpm --dir ui exec vitest run
scripts/`, `check:stack-collection-maps`), all exit 0; every
`@objectstack/spec` `check:*` family, `check:doc-formula-expressions`,
`check:engine-double-contract`, `check:nul-bytes` and
`check:test-source-alias` were re-run after the merge. Five spec checks
exit 3 and `check:generated` exit 1 against the pre-merge `dist`; after
`turbo run build --filter=@objectstack/spec` all six exit 0 and no
artifact changed. `pnpm lint` exit 0. `dispatch-gates --ran`: "112
derived famil(ies) accounted for — 112 run, 0 NOT-MEASURED".
- Delta on `fa5c3fee34` (six files: five anchors, the ADR):
`dispatch-gates --commands` on those paths derives 29 families, all exit
0 (`check:adr-anchors`, `check:adr-links` and its self-test,
`check:adr-index`, `check:adr-symbol-anchors` among them); `--ran`: "29
derived famil(ies) accounted for — 29 run, 0 NOT-MEASURED". `pnpm lint`
exit 0.
- Delta on `1899fc5e98` (after the `000a4c0d` merge, a spec rebuild and
`pnpm install` for main's `playwright-core`): `pnpm
check:test-source-alias` exit 0 (it was exit 1 on the two files before);
`protocol.stored-migration.test.ts` 47 / 47; from `ui/`,
`ObjectFieldInspector.cbpRequiredMaster-12326.test.tsx` 5 / 5.
`dispatch-gates --commands` on the two files derives 54 families, all
exit 0 (`check:type-check-debt` exit 3 first, `@objectstack/scenarios`
unbuilt, exit 0 after building it); `--ran`: "54 derived famil(ies)
accounted for — 54 run, 0 NOT-MEASURED". `pnpm lint` exit 0.
- After the merge, targeted re-runs: spec (`object.test.ts`,
`controlled-by-parent-master.test.ts`, `conversions/`,
`stack-cbp-extension-master.test.ts`, the retired-key pin,
`field.test.ts`) 27 / 27 files, 1415 / 1415 tests; plugin-security
optional-master and sharing suites 2 / 2 files, 63 / 63 tests.
- Every family CI runs on this PR is read from the PR's check runs, not
re-run here.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(`controlledByParentMasterOf` is a pure reading of a field map, beside
the existing refusal.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English. (The `维护者速读` section
is the maintainer's Chinese summary the fleet asks for.)
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_016WV4cxZsyeZedvM4Ea8Ahs)_
<!-- signed: domain:server#1 · os-elon-musk ·
session_016WV4cytLmJxfAmgPVjUbbW -->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 0306761 commit 6106b73
45 files changed
Lines changed: 1666 additions & 236 deletions
File tree
- .changeset
- docs/adr
- packages
- lint/src
- metadata-protocol/src
- objectql/src
- plugins/plugin-security/src
- spec
- api-surface
- export-origins
- src
- conversions
- data
- migrations
- entries/semantic
- scripts
- adr-anchors
- ui/packages/app-shell/src/views
- metadata-admin
- inspectors
- previews
- studio-design
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
| 40 | + | |
39 | 41 | | |
40 | 42 | | |
41 | 43 | | |
| |||
47 | 49 | | |
48 | 50 | | |
49 | 51 | | |
| 52 | + | |
| 53 | + | |
50 | 54 | | |
51 | 55 | | |
52 | 56 | | |
| |||
190 | 194 | | |
191 | 195 | | |
192 | 196 | | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
0 commit comments