Repository navigation
Commit 693352e
docs(qa): 18.0 sweep anchor round 16 — QA round 4's remaining checklist findings and round 15's carried rule-6 texts (#24963)
Refs #24579
Clause-②: no
Round 16 of the 18.0 sweep's wave anchor (claim `6297142170`, PM loop
round 27). It applies three sets of checklist text. First, QA round 4's
remaining checklist-accuracy findings (summary `6288485402`, records
#24838 and #24841, framework `307ce4c4`). Second, round 15's carried
rule-6 texts (PR #24891's acceptance notes). Third, round 14's two text
fixes (report `6289612504` on PR #24857).
Only checklist text changes. 27 items in 11 area files each take one
`revision` bump and one `history` row, and each row names its record and
finding. `RUNNER.md` takes one environment fact: two lines, the entry
and its blank separator. There is no product code, test, pin or
changeset text.
- Base `feb09db14a`. `origin/main` `223ae939bb` is merged in
`79a13e85b7`. It touches no `docs/qa/**` path and none of the sources
these edits cite.
- Every item, pin and table named below was re-read at the base before
it was written. The findings that no longer held are listed under "Items
left alone".
- A scripted span editor made every edit. It is round 15's editor,
extended with an array-element removal and an object append, and it
never re-serialises a file. For each area file it re-parses the result
and asserts four things:
- A1: the new document equals the old one with the declared ops applied.
- A2: every other item and top-level key is value-equal.
- A3: each edited item's changed paths lie inside its declared fields
(array edits by exact index), plus `revision` and the one new history
row.
- A4: the text outside the edited items is byte-identical.
- A whole-branch check against the base agrees: 27 items changed, each
with revision +1 and exactly one appended row, and no other path
changed.
- The new history rows carry the placeholder ref `#PENDING-24579R16`,
because a row's `ref` names this PR and the PR had no number yet. The
next commit on this branch swaps in this PR's number and changes nothing
else.
## What a negative is, and the 14 procedural negatives
A `negative` names a product outcome whose occurrence is the FAIL. The
format documentation already defines it so: the README's item anatomy
calls `negative` "the other side of every gate (deny/absence cases)". So
`RUNNER.md` takes no line for it (ruling 3).
Each of #24841's 14 procedural negatives was handled one of two ways:
- **(a)** Where the procedure guards a product outcome that can be
stated and measured, and that is not already a negative, the negative is
rewritten into that outcome.
- **(b)** Otherwise its text moves, unchanged in substance, into
`steps`. That is the schema's runner-procedure field ("how to exercise
it"). There is no notes field, and `traps` is a closed vocabulary of
trap names. The text is appended, so no step index shifts. Where the
item already carries the same text in a procedure or notes field, the
negative is removed and that carrier is named instead.
| # | item | negative | outcome | where it lives now |
|:--|:--|:--|:--|:--|
| 1 | `automation.packaged-flow-disable-durable` | `negative[3]` | (a) |
FAIL: a ledger-disabled flow answered 409 FLOW_DISABLED with the status
dimension's bare "Flow NAME is disabled" (engine.ts
`describeDisabledFlow`) |
| 2 | `access-security.packaged-flow-write-door-parity` | `negative[1]`
| (a) | FAIL: PUT or DELETE `/api/v1/automation/...` answers 2xx on the
artifact the `/meta` door refused |
| 3 | `access-security.packaged-flow-write-door-parity` | `negative[2]`
| (b) | `steps[6]` |
| 4 | `automation.packaged-flow-subflow-disable-refusal` | `negative[2]`
| (b) | `steps[7]` |
| 5 | `access-security.owd-save-gate` | `negative[2]` | (b) | already in
`fixtures.knownGaps[0]` |
| 6 | `search.rls-both-personas` | `negative[1]` | (b) | `steps[6]`; the
outcome it guards is `negative[0]` |
| 7 | `platform-core.runtime-config-boot-read` | `negative[3]` | (b) |
`steps[6]` |
| 8 | `cli.build-own-contract` | `negative[2]` | (b) | `steps[9]` |
| 9 | `cli.hook-body-extraction-gates` | `negative[4]` | (b) |
`steps[6]` |
| 10 | `cli.migrate-duplicates-inventory` | `negative[2]` | (b) |
`steps[9]`; `negative[3]` is now `negative[2]`, and nothing in the item
cites either by index |
| 11 | `cli.qa-suite-execution` | `negative[4]` | (b) | `steps[12]` |
| 12 | `platform-core.seed-mode-matrix` | `negative[2]` | (b) | already
in `steps[5]` and `acceptance[4]` |
| 13 | `platform-core.seed-mode-matrix` | `negative[3]` | (b) | already
in `fixtures.requires[1]`, which now carries its reason too |
| 14 | `platform-core.marketplace-install-local-lifecycle` |
`negative[5]` | (b) | already in `fixtures.knownGaps[0]` |
Two are (a) and twelve are (b).
## Changes
| finding | item | field | old → new | evidence read at `feb09db14a` |
|:--|:--|:--|:--|:--|
| #24838: no clause for a real 18.0 pre-release database |
`cli.organization-ownership-ceremony` | `acceptance[5]`, `steps[5]`,
`fixtures.requires[2]` added | none → an `18.0.0-next.*` database is
refused at boot, takes the ceremony, and boots with its identity mail,
global setting audit, pending mail jobs and declared webhook carried
over. Each leg is read at its own layer: the boot output, a row read,
the door's answer. The fixture is a database the pre-release's own `os
dev` created, plus HTTP writes; without one the clause is
`blocked(fixture)`. | The item's title already names "an 18.0
pre-release (18.0.0-next.*)" database. `organization-ownership-apply.ts`
`SIBLING_MOVES`: identity mail is `related_object` sys_user plus the
magic link, organization invitations stay, global audit moves. The
mail-job rewrite covers `email.send.async` and
`email.attachment.reclaim`. The oracle is #24838's measured matrix. |
| the same | `platform-core.v18-boot-refusals` | `fixtures.knownGaps[0]`
added | none → a cross-reference to the ceremony item's `acceptance[5]`
| its title scopes a 17.x-shaped database |
| #24841: the expiry leg has no executable step |
`identity-auth.self-service-password-reset` | `steps[5]` | "let the
token expire" → write the token's `sys_verification.expires_at` into the
past with node:sqlite, then expect 400 INVALID_TOKEN and an unchanged
password | `auth-schema-config.ts` `AUTH_VERIFICATION_CONFIG` maps
expiresAt to `expires_at`. No `storeIdentifier` is set, so the row is
keyed `reset-password:TOKEN` in plain text. better-auth 1.7.3's
`consumeVerificationValue` treats a row past expiresAt as invalid. The
TTL is `resetPasswordTokenExpiresIn` or 3600 s. |
| #24841 n3 | `cli.migrate-meta-codemod` | `negative[3]`, `steps[11]`,
`acceptance[7].verify` | "the files the Wrote block names" → those files
plus the declared range's one site. That site is named by its own
"Rewrote the declared protocol range, so the load admits protocol N:"
block, and `--json` reports it as `write.range`. | `meta.ts`
`printWriteOutcome`, `printRangeOutcome` and `writeOutcomeJson` (since
#22252). The block also exists at QA's `307ce4c4`. |
| #24841: plan log line | `cli.migrate-duplicates-inventory` |
`acceptance[3].verify` | "Registered view" only → also the "Overwriting
view" line that the seeded same-name pair prints | `registry.ts`
`registerItem` prints "Overwriting TYPE: KEY" for a held key, then
"Registered TYPE: KEY" on every registration |
| #24841: scratch object needs the namespace prefix |
`platform-core.seed-mode-matrix` | `steps[0]` | no prefix → `NS_SHORT`.
NS is the manifest's namespace, or one derived from the id's last
segment. An unprefixed publish answers 422 NAMESPACE_PREFIX. |
`namespace-prefix.ts` `validateObjectNamespacePrefix` and
`deriveNamespaceFromPackageId`. The package create in `protocol.ts`
derives a missing namespace, and the publish doors judge it (ADR-0129
D2). |
| #24841: the #4409 fixture needs `requires` | `cli.build-own-contract`
| `steps[3]` | no note → keep `requires: ['automation', 'triggers']`, or
`defineStack` refuses an auto-launched flow at load | `stack.zod.ts`
`validateTriggerCapability`. The blank scaffold and the showcase both
declare the pair, so the note covers a trimmed config. |
| #24838: console bundled with server probes | `cli.dev-boot-contract` |
`acceptance[0]` (clause, verify, evidence), `acceptance[7]` added,
`fixtures.requires[2]` re-pointed | health + ready + console → health +
ready. The console is its own clause (screenshot),
`blocked(environment)` on an unbuilt tree. | `console.ts`
`createConsoleNotBuiltPlugin` answers 503 with "The ObjectStack Console
is not built …" |
| #24838: config-declared default tier unstaged |
`cli.dev-boot-contract` | `steps[9]` added, `acceptance[2].verify` | the
tier was named and no step staged it → a staging step, marked as derived
from source and not replayed live | `resolve-project-database.ts`
`readConfigDeclaredDefault` reads the compiled artifact's `{ default:
true, datasource: NAME }` rule and that datasource's `config.filename`,
resolved against the project root. `dev.ts` compiles only when no
artifact exists. |
| #24838: console bundled with server probes |
`platform-core.boot-health` | `acceptance[3]` (clause, verify,
evidence), `acceptance[5]` added | console + app metadata → app
metadata; the console is its own clause | `console.ts` |
| #24841: procedural negatives | the 12 items above | `negative` (and
`steps`, `fixtures.requires[1]` where named) | see the table above |
README item anatomy |
| #24841: environment | `RUNNER.md` | environment facts | + `rsync` is
absent; copy with `tar` or `cp -a` | `command -v rsync` exits 1 here,
while `tar` and `cp` exit 0 |
| PR #24891: MCP clause's unit fallback |
`api-backend.packaged-action-disabled-dispatch` |
`fixtures.knownGaps[0]` | "fall back to the unit pin" →
`blocked(environment)`, with the pin beside it |
`action-activation-dispatch.test.ts` "door 2" calls
`invokeBusinessAction` in-process over stubs, with no MCP transport and
no HTTP |
| PR #24891: engine-pinned arms of api clauses |
`automation.packaged-flow-subflow-disable-refusal` |
`fixtures.knownGaps[0]`, `negative[1]` | "scored from the unit pins" /
"rides the pin" → engine evidence only; the door legs are
`blocked(fixture)` without a scratch caller |
`flow-activation-ledger.test.ts` runs over a fake store |
| PR #24891: a log clause's half | `cli.datasource-introspect-codegen` |
`acceptance[0].verify`, `fixtures.knownGaps[2]`, `steps[1]` | "scored
from the pins" → `blocked(fixture)`, with the pins beside it |
`remote-tables-twin.equivalence.test.ts` (real Hono adapter), the
service test, `envelope-unwrap.test.ts` (the CLI over a stubbed fetch) |
| PR #24891: wire-shape clauses |
`integration-system.connector-auth-kind-application` |
`acceptance[0].verify`, `fixtures.knownGaps[0]` | "unit fallback" →
`blocked(fixture)`, with the pin beside it | `rest-connector.test.ts`
injects a fetch stub (`fetchImpl`) |
| PR #24891: legs scored from pins, mixed oracles |
`approvals.email-action-token-door` | `fixtures.knownGaps[0]`,
`fixtures.knownGaps[2]` | every live-token leg "scored from the pins" →
each leg at its own clause's layer |
`action-page-member.integration.test.ts` runs a started kernel
(driver-sql, in-memory SQLite) on a real Hono app and mints `ttlMs: 1`
for the expired page. `approval-service.test.ts` pins the reasons. |
| PR #24891: the same | `approvals.act-door-and-admin-rescue-slate` |
`fixtures.requires[1]`, `acceptance[1]` (verify, evidence),
`automated.ref` | scored from the dispatcher and member pins → each at
its layer, and the ref gains
`packages/qa/http-conformance/src/hono-approvals-act.conformance.test.ts`
| That pin drives `createHonoApp` → `HttpDispatcher` → the real
`ApprovalService` member with a live token: "GET renders the confirm
page for a live token; a form POST redeems it". `approvals-act.test.ts`
uses a stub member. |
| PR #24891: ref scope | `integration-system.datasource-admin-lifecycle`
| `automated.ref`; `acceptance[3]` (verify, evidence); `steps[8]` |
"pins route behavior + envelope" → per clause: `acceptance[7]`,
`negative[4]` and `negative[5]` whole, and route-layer halves of
`acceptance[0]`, `[2]`, `[4]`, `[6]` and `[8]`. The bind arm, once
"scored from the unit pin", is now `blocked(fixture)` with the pin
beside it. | Both ref pins mount `registerDatasourceAdminRoutes` on the
real `HonoHttpServer` over stubbed services and fake auth.
`datasource-credential-migration.test.ts` drives the planner over row
objects. |
| PR #24891: the REST wording under a `test` oracle |
`records-forms.field-unique-enforcement` | `acceptance[4].clause`,
`automated.ref` | "answering the same 409 UNIQUE_VIOLATION over REST" →
the refusal satisfies `isUniqueViolationError`, measured at SqlDriver
through `createAsApi`, never over a REST door | The SQL pins'
`createAsApi` and `@objectstack/types` `isUniqueViolationError`;
objectql `envelopeUniqueViolation`. `acceptance[0]` to `[3]` drive the
REST door live, so no knownGaps line is owed. |
| PR #24857's report | `records-forms.crud-roundtrip` |
`acceptance[4].verify` | "showcase Project" → `showcase_account`, the
showcase's `enable.trackHistory` object | objectui
`e2e/live/record-history-display.spec.ts`, blob `7ebc45106cff` at the
console pin `616df6598b42` |
| PR #24857's report | `studio-authoring.first-run-loop` |
`acceptance[0].verify` | F1 "targets testids no source declares" → F1
targets `studio-pkg-chip`, `package-form-id-input` and
`package-form-submit`, all declared at the pin, so it is citable when
run there | objectui `e2e/live/studio-object-designer.spec.ts`, blob
`58a7f1cbf024`; a `git grep` at `616df6598b42` finds them in app-shell's
`StudioDesignSurface.tsx` and `PackageFormDialog.tsx` |
## Items left alone, with reasons
- `automation.trigger-status-contract` `steps[5]` (#24841): no longer
holds. Revision 4 (#24824) already spells `{"inputs": {"new_assignee":
"…"}, "output": {"$x": 1}}`. Re-read at the base: the wizard's screen
requires `new_assignee`, and `refuseInvalidScreenInput` runs before
`applyResumeSignal` (engine.ts).
- `identity-auth.self-service-password-reset`, the `sys_email` half
(#24841): no longer holds. Revision 4 (#24824) moved `requires`,
`steps[1]`, `knownGaps[0]` and `acceptance[0]` to `sys_identity_email`,
and the remaining `sys_email` mentions say "never sys_email".
`IDENTITY_EMAIL_OBJECT` is `'sys_identity_email'`
(email-log-objects.ts).
- `cli.migrate-duplicates-inventory`, the index-drop half (#24841): no
longer holds. Revision 9 (#24850) already says to drop
`uniq_OBJ_organization_id_FIELD` first (schema-drift.ts
`buildIndexName`).
- `RUNNER.md`, a line defining a negative: not added, because the
README's item anatomy already defines it.
- A fresh-install clause for #24827's order (`os migrate apply` first,
then `os serve`): not added. `platform-core.v18-boot-refusals` scopes
the refusal of a 17.x-shaped database and has no fresh-install clause,
so it is a candidate for the anchor. The change ships in **18.1.0**:
`@objectstack/cli` is in the changesets `fixed` group at 18.0.0,
`.changeset/24827-migrate-apply-birth-marker.md` is `minor`, and no
pending changeset is `major`. It is unpublished at the base (npm
`latest` is 18.0.0).
- `records-forms.crud-roundtrip` `acceptance[6]`: its readonly half says
"Score that half from the pins in `automated`, never as
blocked(fixture)", under an `api` oracle, from a protocol pin over a
stub engine and an engine pin. That is the rule-6 class, but it is not
in this round's list, and changing a P0 clause's verdict rule is the
anchor's call. Carried.
- `cli.datasource-introspect-codegen` `automated.ref` still names
"acceptance[0]'s NARROWING half ONLY". Under round 15's reading no pin
holds a full clause, so `automated` would go and a knownGap would
replace it. The dispatch named the spelling only. Carried.
- `records-forms.record-discussion-mentions` `knownGaps[0]` /
`personas[1]` (anchor comment `6290946648`, "a second signed-up user"):
still present at the base, and not in this dispatch. Carried.
- Not this round, carried by the PM: the fixture gaps of #24838 and
#24841, the `studio-editor` / `form-view-subforms` widening candidates,
and round 12's 80 "showcase isolated" spellings.
## PM hypotheses, measured
1. "Most of the 14 are (b), fewer than five (a)." This holds: 12 are (b)
and 2 are (a).
2. "`platform-core.v18-boot-refusals` is the home, and the ceremony item
takes a cross-reference." This does not hold.
`cli.organization-ownership-ceremony`'s title names "an 18.0 pre-release
(18.0.0-next.*)" database, the moves, the webhook settlement and the
boot that refuses or serves. `platform-core.v18-boot-refusals`' title
names a 17.x-shaped database. So by the ruling ("the item whose scope it
is") the clause sits in the ceremony item, and the boot-refusals item
takes the cross-reference.
## Acceptance notes
- Some edits were located beyond the dispatch's field list, inside items
it names. They were applied because they are the same rule-6 class and
the item would otherwise contradict its own edit:
- `automation.packaged-flow-subflow-disable-refusal` `negative[1]`;
- `cli.datasource-introspect-codegen` `steps[1]` and
`fixtures.knownGaps[2]`;
- `approvals.email-action-token-door` `fixtures.knownGaps[2]`;
- `approvals.act-door-and-admin-rescue-slate` `acceptance[1]` and
`automated.ref`;
- `integration-system.datasource-admin-lifecycle` `acceptance[3]` and
`steps[8]`;
- `records-forms.field-unique-enforcement` `automated.ref`, whose
sentence quoted the old clause wording;
- `cli.dev-boot-contract` `fixtures.requires[2]` (re-pointed to
`acceptance[7]`) and `acceptance[2].verify`.
- `integration-system.datasource-admin-lifecycle` took its edit in two
commits on this branch. The net diff against the base is one revision
bump and one history row.
- The ceremony's `acceptance[5]` rests on #24838's measurements and on
the source. Its `steps[5]`, and `cli.dev-boot-contract` `steps[9]`, were
not replayed live here. `steps[9]` says so.
- A configured TTL exists: the spec declares
`auth.emailAndPassword.resetPasswordTokenExpiresIn`, and auth-manager.ts
forwards it to better-auth. It was not used for the expiry step. Its
plumbing from a stack config was not measured, it has no liveness row,
and it would still need a wait.
- `hono-approvals-act.conformance.test.ts` was read, not run, here. The
objectui specs were read at the console pin `616df6598b42` with a
read-only `git show`. Their verdicts at the pin are NOT MEASURED.
- `integration-system.datasource-admin-lifecycle`: two pins are not in
the ref, `admin-routes-auth-guard.test.ts` (the floor) and
`datasource-route-ledger.conformance.test.ts` (cited by
`acceptance[8].verify`). Whether either holds a whole clause at its
layer was not measured.
## Gates
The derived list is `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`, computed on the actual change set:
12 paths against merge base `223ae939b`, at head `e208b693b5`. It gives
13 commands. Each one ran at that head with its exit code captured
before any pipe, and each exited 0:
`node scripts/check-ci-filter-parity.mjs` · `node
scripts/check-closing-keyword-parity.mjs` · `node
scripts/check-closing-keyword-parity.mjs --self-test` · `node
scripts/check-comment-mask-corpus.mjs` · `pnpm --filter
@objectstack/lint run check:doc-formula-expressions` · `pnpm
check:cross-package-test-inputs` · `pnpm check:doc-authoring` · `pnpm
check:driver-memory-census` · `pnpm check:gitlink-declared` · `pnpm
check:nul-bytes` · `pnpm check:platform-checklist` · `pnpm
check:refd-timer-probe` · `pnpm check:watch-hint-literal`
The first run of `check:doc-formula-expressions` (at `79a13e85b7`)
refused with exit 3, a prerequisite, because `@objectstack/formula` and
`@objectstack/lint` were not built. Both were built under the shared
verify lock (`VERDICT command-exit 0`, held 116 s, waited 0 s), and the
rerun exited 0. The `--ran` reconciliation reads "13 derived, 13 run, 0
NOT-MEASURED, 0 UNRUN".
No per-PR CI job runs `pnpm check:platform-checklist`, so this local
reading is its evidence:
| reading | base `feb09db14a` | head `e208b693b5` |
|:--|:--|:--|
| items | 16 areas, 328 items (323 active, 1 planned) | 16 areas, 328
items (323 active, 1 planned) |
| line citations | 0 survive across 21 swept documents | 0 survive
across 21 swept documents |
| symbol anchors | 965/975 against 430 cited sources, 10 on the named
residual (#16898), 18 file floors held | 975/985 against 433 cited
sources, 10 on the named residual (#16898), 18 file floors held |
The ten new anchors all resolve, and the residual is unchanged.
`git merge-tree --write-tree origin/main HEAD` exited 0 against
`223ae939bb`, and again against `19e5338cc8`, which touches no
`docs/qa/**` path. No open PR touches `docs/qa/platform-checklist/`: all
16 open PRs' file lists were read in full, every page. So there was no
second merge-tree to run.
There is no ablation, because no tested behaviour changed.
`skip-changeset` was re-measured: `docs/qa/**` appears in none of the 73
non-private manifests' `files[]` (86 tracked `package.json` files), and
the repo root is private.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 5f11cec commit 693352e
12 files changed
Lines changed: 250 additions & 106 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
341 | 341 | | |
342 | 342 | | |
343 | 343 | | |
| 344 | + | |
| 345 | + | |
344 | 346 | | |
345 | 347 | | |
346 | 348 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1406 | 1406 | | |
1407 | 1407 | | |
1408 | 1408 | | |
1409 | | - | |
| 1409 | + | |
1410 | 1410 | | |
1411 | 1411 | | |
1412 | 1412 | | |
| |||
1465 | 1465 | | |
1466 | 1466 | | |
1467 | 1467 | | |
1468 | | - | |
1469 | | - | |
| 1468 | + | |
1470 | 1469 | | |
1471 | 1470 | | |
1472 | 1471 | | |
| |||
1494 | 1493 | | |
1495 | 1494 | | |
1496 | 1495 | | |
1497 | | - | |
| 1496 | + | |
| 1497 | + | |
1498 | 1498 | | |
1499 | 1499 | | |
1500 | 1500 | | |
| |||
2677 | 2677 | | |
2678 | 2678 | | |
2679 | 2679 | | |
2680 | | - | |
| 2680 | + | |
2681 | 2681 | | |
2682 | 2682 | | |
2683 | 2683 | | |
| |||
2699 | 2699 | | |
2700 | 2700 | | |
2701 | 2701 | | |
2702 | | - | |
| 2702 | + | |
| 2703 | + | |
2703 | 2704 | | |
2704 | 2705 | | |
2705 | 2706 | | |
| |||
2729 | 2730 | | |
2730 | 2731 | | |
2731 | 2732 | | |
2732 | | - | |
2733 | | - | |
| 2733 | + | |
2734 | 2734 | | |
2735 | 2735 | | |
2736 | 2736 | | |
| |||
2751 | 2751 | | |
2752 | 2752 | | |
2753 | 2753 | | |
2754 | | - | |
| 2754 | + | |
| 2755 | + | |
2755 | 2756 | | |
2756 | 2757 | | |
2757 | 2758 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1565 | 1565 | | |
1566 | 1566 | | |
1567 | 1567 | | |
1568 | | - | |
| 1568 | + | |
1569 | 1569 | | |
1570 | 1570 | | |
1571 | 1571 | | |
| |||
1581 | 1581 | | |
1582 | 1582 | | |
1583 | 1583 | | |
1584 | | - | |
| 1584 | + | |
1585 | 1585 | | |
1586 | 1586 | | |
1587 | 1587 | | |
| |||
1694 | 1694 | | |
1695 | 1695 | | |
1696 | 1696 | | |
| 1697 | + | |
| 1698 | + | |
| 1699 | + | |
| 1700 | + | |
| 1701 | + | |
| 1702 | + | |
1697 | 1703 | | |
1698 | 1704 | | |
1699 | 1705 | | |
| |||
0 commit comments