Repository navigation
Commit 316be32
Fixes #21594
Clause-②: yes (narrowing)
An app-authored body (a sandboxed hook, action or job body) may no
longer read the stored-metadata tables (`sys_metadata`,
`sys_metadata_history`). Every read verb answers the body boundary's
`PERMISSION_DENIED` / 403 before it runs, with a prescription naming the
metadata API's read route. With the binding and write refusals already
landed, an app-authored body now reaches these tables through the
metadata API only. This is the maintainer's ruling, letter B (record
`5974479930`).
An action body is not **handed** a family row either. The `/actions`
door loads an action's subject record before it dispatches. When that
record is a family row, the call now answers the same 403 before the
body runs, instead of handing the body the row as `ctx.record`. That
covers an action declared on a family table and an object-less action
addressed under one. The contract review's flag 2 raised this path; it
was measured first, at the doors (below).
**The handler contexts (A2) are ruled A by the maintainer** (record
`5978653398`, 「同意」 on director batch #276). Ruling B covers sandboxed
app bodies only; app host handlers registered with `registerAction` keep
the projected read. This PR leaves them exactly as they were: their
`ctx.api`, `ctx.engine.find` and subject record are unchanged. C (a
host-code trust model at the engine entry) is not taken and not filed.
**The census's cloud leg is answered.** The same ruling record cites
triage's reading `5976321402`: zero app-authored readers of the family
in `objectstack-ai/cloud`, neither bodies nor host handlers. That
reading is triage's, not this container's.
## What changed
- **`packages/runtime/src/stored-metadata-body-boundary.ts`.** Adds
`storedMetadataBodyReadRefusal(object, verb)`. It uses the write
refusal's own envelope (`STORED_METADATA_BODY_BOUNDARY_CODE` /
`_STATUS`, `PERMISSION_DENIED` / 403), so there is no new error code.
Its prescription names the read route: `GET /api/v1/meta/:type/:name`,
and `.../history` for versions. The binding and write refusals' text is
byte-unchanged; the shared constructor takes the prescription as a
defaulted parameter.
- **`packages/runtime/src/stored-metadata-reader-seam.ts`.** Adds a body
READ layer, `refuseStoredMetadataBodyReads`, on the same derive walk as
the write layer: `object()`, `sudo()`, `withRunAs()`, a
`transaction(fn)` callback and `beginTransaction()`. It refuses exactly
the read verbs the seam serves (`find`, `findOne`, `count`,
`aggregate`), before the verb runs and before its query is looked at. So
a refused read gives the same answer whatever its filter, sort,
grouping, search or projection names: no rows, and no oracle. The write
layer (`refuseStoredMetadataBodyWrites`) is unchanged in code. It sits
over the read layer and passes reads down to it; only its comments now
say so.
- **`packages/runtime/src/sandbox/body-runner.ts`.** `buildSandboxApi`
is the one place every body face gets its API. It is now
`refuseStoredMetadataBodyWrites(refuseStoredMetadataBodyReads(source))`.
- **The subject record (this round).**
`stored-metadata-body-boundary.ts` adds
`storedMetadataBodySubjectRecordRefusal(object, action)`. It uses the
same envelope (`PERMISSION_DENIED` / 403, operation `record`) and the
same read prescription. `sandbox/body-runner.ts` consults it first in
`actionBodyRunnerFactory`'s bound handler, before the sandbox context is
built and before the body runs. That handler is the one point every
action body passes through to run, whichever door bound it, and the one
place the handler is known to be a body. The subject is the door-stamped
routed object (`params.objectName`, which both action doors write after
the caller's params), else the declared object. A record is handed when
the call carries a record id or a non-empty record. A family-routed call
with neither hands the body nothing and runs.
- **Deleted: the read-side serve for bodies.**
`serveStoredMetadataReadsThrough` no longer wraps a body's API in
`buildSandboxApi`. With every family read and write refused first, it
served a body nothing. It is removed, not kept beside the refusal.
Nothing else became dead: the seam's projection, evaluate refusals,
default-search narrowing and write-return serve still serve the
host-handler contexts (ruling A keeps them). No exported symbol is
deleted or renamed.
- **`packages/runtime/src/action-execution.ts`.** Comment only
(`buildActionApi`): an action body's API is built over this context, and
the body layers refuse first.
- **`.changeset/21594-body-family-read-refusal.md`.**
`@objectstack/runtime` `minor`, BREAKING (narrowing), exactly one
ADR-0087 marker (`not-required (no-migration-prescription)`), in the
shape of the evaluate-refusal changeset. It states the route, and which
earlier entries of this release it supersedes for bodies. This round
adds a **Subject record** bullet, and names a host handler's subject
record among the unchanged.
- **`scripts/engine-double-contract.pinned.json`.** One row for the new
unit pin's double, whose `findOne` routes through the engine's own
predicate (`check-engine-double-contract.mjs --write`).
## Measured first
### A1. Census of app-authored readers (the stop condition): 0 readers
- **objectstack `examples/**`** at `15fe567c9c`:
- **Family table names in every tracked file:** 3 hits, all prose: the
`app-showcase` changelog (2) and one code comment in
`app-showcase/src/system/connectors/index.ts`.
- **Indirect spellings:** `SystemObjectName.METADATA`,
`STORED_METADATA_BODY_OBJECTS` and `isStoredMetadataBodyObject` have 0
hits. A non-literal `.object(...)` argument has 0 hits.
- **Literal `.object(...)` targets:** four `showcase_*` objects.
- **Non-literal engine reads:** three, in host seed code on the engine
directly (`bind-position-sets.ts` ×2, `seed-approval-demo.ts`). Each is
called only with non-family objects.
- **Population:** 14 files carry a `body:` key; 5 touch `ctx.api`,
`ctx.engine` or `registerAction`.
- **hotcrm** at `4054ec2680` (a public shallow clone, read only, 924
tracked files):
- **Family table names:** 5 hits, all prose or test comments. None is in
`src/**` or `apps/**`.
- **Non-literal `.object(...)` reads:** 7, in hook bodies. Each is bound
to a local list of `crm_*` objects or to the hook's own object.
- **Literal targets:** 21 distinct, all `crm_*` or non-family `sys_*`
objects.
- **Host action handlers:** 0. Its `registerAction` appears in a comment
only.
- **cloud** (`objectstack-ai/cloud`): **NOT MEASURED from this
container** (private, unreachable). Answered by triage's reading
`5976321402`, as the ruling record `5978653398` cites: zero app-authored
readers.
### Flag 2 (this round). The subject record: can a body be handed a
family row?
Measured at the doors on the head before the fix (`d5b890226c`), with a
temporary probe that was not committed. Each body only returned what it
was handed as `ctx.record`; the probe recorded classes (keys present,
body column type, hash form), never values.
- **The app manifest / `defineStack` / `os validate`.** A strict
`defineStack` (the default) refuses an action whose `objectName` is a
family table: `STACK_CROSS_REFERENCE_INVALID`, because the table is not
an object the stack defines. That is a generic cross-reference check,
not the family boundary. `defineStack(…, { strict: false })` accepts it:
`os validate` answers `valid: true`, exit 0, with placement warnings
only. An object-less body action is accepted in both modes. Measured
with `bin/run-dev.js validate --json`.
- **Install-local** (`POST /api/v1/marketplace/install-local`). The
package with a family-bound action and an object-less body action
installs (200, `success: true`). It binds both handlers,
`sys_metadata:…` and `global:…`, through the runtime's one binder.
Handed a door-shaped context carrying a family row, each body received
it. Measured in `@objectstack/cloud-connection` against the built
runtime.
- **The `/meta` action save door.** `PUT /meta/action/:name` with
`objectName` set to a family table answers 200. Once bound, `POST
/actions/sys_metadata/:name/:id` handed its body the row.
- **Boot** (an `AppPlugin` over a JSON bundle, the composition `os start
--artifact` builds). Both actions bind.
- **The invoke doors.**
- **REST `/actions`, as the administrator.** The body received the
door-served family row on both tables: the body column as its projection
and the hash in keyed form, with no stored credential. That held for an
action declared on the table, for an object-less body action addressed
under it, and for the `/meta`-declared action. No family declaration is
needed for the object-less route; any installed object-less body action
can be addressed under a family table. **Reachable.**
- **REST `/actions`, as a member.** `404 RECORD_NOT_FOUND`: the door's
own subject load, under the caller's read scope, stops it, and the body
never runs.
- **MCP `run_action`.** Not reached. The door refuses an action on any
`sys_*` object before the record load, and resolves an object-less
action only under its own key. The composed kernel's metadata service
lists no standalone action, so this door is pinned at unit level.
- **A host handler** under the same route received the projected row
(unchanged, ruling A).
- **An ordinary object's action** received its row (unchanged).
**Reachable, so it is refused in this card.** The seam is the action
body's own handler, not the door: the doors dispatch body and host
handlers alike and cannot tell them apart at the prefetch. A
binding-time refusal, the parallel of the hook-binding refusal, would
not see the object-less route, so it is not the only coherent seam; it
would also be insufficient. After the fix, on `f5d575d072`, the same
probe gave:
- **REST `/actions`, administrator:** `403 PERMISSION_DENIED` naming the
metadata API, for every family case (both tables, the family-bound
action, the object-less route, the `/meta`-declared action).
- **Host control and ordinary control:** 200, unchanged.
- **Family-routed call with no record:** 200, nothing handed.
- **Member:** still `404` at the door.
- **Install-local's bound handlers:** `PERMISSION_DENIED` / 403.
### A2. Which seam contexts carry app-authored code
- **① The sandboxed body (`buildSandboxApi`).**
- **Faces and doors:** hook bodies on every data door that fires hooks;
action bodies on REST `/actions`, MCP `run_action` and an engine
`execute`; job bodies on the job scheduler.
- **Authorship:** always app-authored (a code bundle, an installed
artifact or the metadata door). The write refusal reaches it.
- **Here:** refused.
- **② `ctx.engine.find` and ③ `ctx.api` of an action handler
(`buildActionEngineFacade`, `buildActionApi`).**
- **Doors:** built at two only, REST `/actions` (`domains/actions.ts`)
and MCP `run_action` (`action-execution.ts`).
- **Platform registrants:** in `packages/**`, the only `registerAction`
callers are the two body runners (the objectql metadata-service bind and
`app-artifact-handlers.ts`). Their handlers are sandboxed bodies, which
never see ② and get ③ only as the source the body layers wrap.
- **App registrants:** host-code handlers come from apps.
`examples/app-todo` registers 8 host handlers from its `onEnable(ctx)`
through `ctx.ql`; hotcrm registers none.
- **Reach of the write refusal:** it does not reach these handlers, as
pinned by its own unit case ("the read seam ALONE — a host code
handler's ctx.api — keeps its family writes").
- **Verdict:** ruled A (`5978653398`). Left served, unchanged.
- **Platform internal readers through any seam context: 0.** Every
platform reader of the family in `packages/**` reads through the engine
or a driver directly. That covers the metadata protocol, the objectql
plugin, the core translation fallback, the flow credential channel and
the CLI. None reads through a body API or a handler context. Pinned
unaffected:
- the generic data door;
- the metadata API (the route the refusal prescribes);
- the engine's own read of the stored form;
- handler ② / ③, still served projected and keyed.
### A3. The envelope
- **Reuse:** the read refusal rides the same envelope,
`PERMISSION_DENIED` / 403, with `object` and `operation` set. There is
no new error code.
- **Verbs:** every read verb the seam serves is refused (`find`,
`findOne`, `count`, `aggregate`). Search is a query shape on a read, and
is refused with it.
- **Pin:** ten query shapes × four verbs give one answer per verb.
### A4. The deletion and the ledgers
- **Deleted:** the body serve wrap only. No exported runtime symbol is
deleted or renamed. Two module exports are added
(`refuseStoredMetadataBodyReads`, `storedMetadataBodyReadRefusal`);
neither is on the package entry.
- **Ledger grep:** the touched symbols across
`packages/spec/liveness/**`, every `*.ledger.*` file,
`scripts/engine-double-contract.pinned.json`, `content/docs` and `docs`.
One hit: a liveness note in `hook.json` that cites `buildSandboxApi`
reading `ctx.api` from the engine context. That is still true, and the
function keeps its name.
- **`packages/metadata-protocol`:** no edit. No symbol there is left
without a consumer. The seam still consumes each one for the handler
contexts, as counted in the report.
- **Docs:** `git grep` over hand-written `content/docs` and published
`skills/` found no page saying a body can read the family's tables. Zero
hits, so nothing was touched.
### A5. Reverse verification (ablation)
- **Mutation:** made with `scripts/ablation-replace.mjs` in WRAP mode on
`packages/runtime/src/stored-metadata-reader-seam.ts`, from the
committed state (`9c87884191`).
- **Anchor:** `if (BODY_FAMILY_READS.has(prop)) {`, hit ×1 → ×0.
Replaced by `if (false && BODY_FAMILY_READS.has(prop)) {`, ×0 → ×1.
- **Blob:** `76eabe5afe1a` → `c56dca3ae3e6`.
- **Expected direction:** red, the usual one. Observed: red.
- **Result:** 18 red, 86 green, over six files.
- **Red:** every read-refusal pin. That is 10 in the new unit file and
the 8 body cases of the reader-contexts integration pin.
- **What the red showed:** with the refusal ablated, the action bodies
fell through to the handler-served read. The hook body's insert landed
carrying the stored form, so the refusal is now the hook face's only
guard, as the ruling's deletion intends.
- **Green, unchanged:** every write and hook-binding pin
(`stored-metadata-body-writes.test.ts`,
`stored-metadata-body-boundary.test.ts`,
`stored-metadata-body-boundary.pin.test.ts`), the reader-seam unit file,
the handler ② / ③ cases and the platform-reader controls.
- **Restore:** blob after restore == HEAD blob (`76eabe5afe1a`), `git
diff HEAD` empty, `git status --porcelain` empty.
- **Second leg (this round), the subject-record refusal.**
- **Mutation:** on `packages/runtime/src/sandbox/body-runner.ts` at
committed `5e8fd37d78`. Anchor `if (subjectRefusal) throw
subjectRefusal;`, hit ×1 → ×0. Replaced by `if (false && subjectRefusal)
throw subjectRefusal;`, ×0 → ×1.
- **Blob:** `522b737bc800` → `3eb2d928aba3`.
- **Expected direction:** red. Observed: red, 7 failed and 114 passed
over the same six files.
- **Red:** the 5 unit subject-record refusal cases, and 2 integration
cases (the administrator's family-bound and object-less routes; the
`/meta`-declared action).
- **Green:** every control (the host handler, the ordinary record, the
family-routed call with no record, the member's door-level 404), the
MCP-door pins, every read-layer pin, and every write and hook-binding
pin.
- **Restore:** blob == HEAD (`522b737bc800`), `git diff HEAD` empty,
`git status --porcelain` empty.
## The handler contexts (A2): ruled A
**Ruling `5978653398`, letter A** (maintainer 「同意」 on director batch
#276): ruling B covers sandboxed app bodies only, and app host handlers
registered with `registerAction` keep the projected read. The record's
stated cost: the reader-context seam's projection and narrowing code
stays, for host handlers. The analysis that went to the maintainer is
kept below.
**Question.** Should the read refusal also reach an action handler's
`ctx.api` and `ctx.engine.find`, the host code an app registers with
`registerAction`? Or do those stay outside, the same context set as the
write refusal?
- **A (recommended): keep them outside, the same set as the write
refusal.**
- Host code holds the engine itself: it registers its handlers through
`ctx.ql` in `onEnable`. A refusal on its `ctx.api` would declare a
boundary the runtime cannot enforce.
- The write ruling already drew this line for writes.
- Pull is zero either way: the 8 example host handlers read no family
table, and hotcrm has none.
- **B: widen the read refusal to the handler contexts.**
- This would make the rest of the seam's serve code dead and deletable:
the projection, the evaluate refusals, the narrowing and probably the
write-return serve.
- It would also split host code's rules: writes allowed, reads refused.
That holds unless the write ruling is reopened for host code too.
## Tests
All at the final head `f5d575d072`: the merge of `origin/main` at
`100f68b77f` into this branch, as a merge commit, built whole (`pnpm
build --concurrency=2`, 72 of 72 tasks, none cached).
- **Unit pins:** `src/stored-metadata-body-reads.test.ts`, 26/26. The
first round's 15 cases are unchanged. This round adds:
- **Five subject-record cases** on the real QuickJS action face: a
family-bound action, and an object-less action routed under each table,
are refused with the envelope and never run. With no routed object, the
declared family object stands in. Controls: an ordinary subject record
is handed as before, and a family-routed call with no record runs.
- **Four MCP-door cases:** a family-bound action is refused before any
record load or dispatch, and an object-less action named under a family
table does not resolve.
- **Public-door integration pin:**
`src/stored-metadata-reader-contexts.pin.test.ts` (REST `/actions`, the
data door, `/meta`), on the built packages, 27/27. This round adds six
cases:
- the administrator's family-bound and object-less routes on both
tables, refused 403 naming the metadata API;
- the `/meta`-declared family-bound action, refused once bound;
- the member stopped at the door with `404 RECORD_NOT_FOUND`;
- control: the host handler's subject record still served like the data
door;
- control: an ordinary subject record handed as before;
- control: a family-routed call with no record runs.
- **Family pins together:** with the write and boundary pins and the
reader-seam unit file, 6 files, 121/121.
- **Full `@objectstack/runtime` suite:** `--project local` 322 files,
4591 passed and 19 skipped; `--project repo` 3 files, 751 passed. The
two runs were joined with `&&` under one lock verdict, command-exit 0.
- **Typecheck:** `pnpm --filter @objectstack/runtime typecheck` passes,
`check:test-typecheck` OK.
- **Lint:** full `pnpm lint` exits 0 with no findings.
- **Liveness evidence:** `pnpm --filter @objectstack/spec exec vitest
run --project repo scripts/liveness/evidence.test.ts`, 42/42.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` gives 71 families against merge base
`100f68b77`, all exit 0, with `check:dual-build-cjs-loads` after the
full build. Reconciled with `--ran`: "71 derived, 71 run, 0
NOT-MEASURED, 0 UNRUN", every exit code recorded.
## Acceptance notes
- **The changesets of this release now overlap.** The pending
reader-seam changesets (served read; evaluate refusals) and the
binding-and-write changeset's "Unchanged: a body's reads" bullet
describe the body context as served. This PR's changeset says it
supersedes them for bodies. If they should read clean on their own, the
binding-and-write changeset's bullet is the one line to amend before the
release compiles. Carrier: the release seat.
- **A spec comment is incomplete.** The header of
`packages/spec/src/kernel/stored-metadata-body-objects.ts` lists what
the runtime refuses for a body as binding and writing; it does not
mention reading. That file is outside this lane's fence
(`packages/spec`). Carrier: none.
- **The refusal is now the hook face's only guard.** The deletion leaves
nothing behind it, as the ruling intends; the ablation measured this.
Both the unit pin and the integration pin go red if it is removed.
- **The base is merged.** `origin/main` at `100f68b77f` was merged into
the branch as a merge commit (`f5d575d072`), with no rebase and no
force-push. It merged cleanly. Main's change to `sandbox/body-runner.ts`
(the job face's organization envelope) still builds its API through
`buildSandboxApi`, so the job face keeps both body layers.
- **A family-bound action still installs and binds.** Install-local, the
`/meta` door and a lax `defineStack` all accept an action declared on a
family table, and the body runner binds it. The refusal lands at run
time, when a family row would be handed over. A binding-time refusal
(the parallel of the hook-binding refusal) was not added: it could not
see the object-less route, which needs no family declaration, and the
run-time refusal covers both. Whether binding should also refuse is not
this card's question. Carrier: none.
---
_Generated by [Claude
Code](https://claude.ai/code/session_016GiHYRmLSNWTfbX9gVQkpz)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent a2b7328 commit 316be32
9 files changed
Lines changed: 935 additions & 183 deletions
File tree
- .changeset
- packages/runtime/src
- sandbox
- scripts
| 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 | |
|---|---|---|---|
| |||
1500 | 1500 | | |
1501 | 1501 | | |
1502 | 1502 | | |
1503 | | - | |
1504 | | - | |
1505 | | - | |
| 1503 | + | |
| 1504 | + | |
| 1505 | + | |
| 1506 | + | |
| 1507 | + | |
1506 | 1508 | | |
1507 | 1509 | | |
1508 | 1510 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
64 | | - | |
| 64 | + | |
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
| 69 | + | |
69 | 70 | | |
70 | 71 | | |
71 | 72 | | |
| |||
448 | 449 | | |
449 | 450 | | |
450 | 451 | | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
451 | 457 | | |
452 | 458 | | |
453 | 459 | | |
| |||
478 | 484 | | |
479 | 485 | | |
480 | 486 | | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
481 | 519 | | |
482 | 520 | | |
483 | 521 | | |
| |||
970 | 1008 | | |
971 | 1009 | | |
972 | 1010 | | |
973 | | - | |
974 | | - | |
975 | | - | |
976 | | - | |
977 | | - | |
978 | | - | |
979 | | - | |
980 | | - | |
981 | | - | |
982 | | - | |
983 | | - | |
984 | | - | |
985 | | - | |
986 | | - | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
| 1014 | + | |
| 1015 | + | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
| 1023 | + | |
| 1024 | + | |
| 1025 | + | |
| 1026 | + | |
| 1027 | + | |
| 1028 | + | |
| 1029 | + | |
987 | 1030 | | |
988 | 1031 | | |
989 | 1032 | | |
990 | | - | |
| 1033 | + | |
991 | 1034 | | |
992 | 1035 | | |
993 | 1036 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
15 | 17 | | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
19 | | - | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
20 | 30 | | |
21 | 31 | | |
22 | | - | |
23 | | - | |
24 | | - | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
25 | 35 | | |
26 | | - | |
| 36 | + | |
27 | 37 | | |
28 | 38 | | |
29 | 39 | | |
| |||
37 | 47 | | |
38 | 48 | | |
39 | 49 | | |
40 | | - | |
| 50 | + | |
41 | 51 | | |
42 | 52 | | |
43 | 53 | | |
44 | 54 | | |
45 | 55 | | |
46 | | - | |
47 | | - | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
48 | 64 | | |
49 | 65 | | |
50 | 66 | | |
| |||
105 | 121 | | |
106 | 122 | | |
107 | 123 | | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
108 | 162 | | |
109 | 163 | | |
110 | 164 | | |
| |||
0 commit comments