Repository navigation
Commit 10f27b6
domain:server#3 · os-bill · session_01Utp6CXc5hequf76uJfzrdK
<!-- claude-projects-attribution: {"github_login":"os-bill"} -->
_Requested by **Bill** · [project
thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgKURKuRGF1ewEVVZKx7U1i4)_
Part of #27123
Ruling-ref: 6467932167
Clause-②: no — no authorable key or accepted value changes;
`packages/spec` gains the `sys_trusted_key` row of
`PLATFORM_OBJECTS_BY_PACKAGE['platform-objects']` (the registry's
maintenance contract, a platform-object name list) and its liveness
`integrity` row moves to `live`; the rest is TSDoc prose
Metadata-protocol: none
## 维护者速读
- **改了什么:** 新增 `os plugin keygen`、`os plugin trust add|list|revoke`、`os
plugin install ./x.osplugin`,以及部署级系统对象 `sys_trusted_key`(只存公钥)。install
先按清单 `integrity` 逐文件复核,再用库里该 key id 的公钥验 `.sig`(`verifyPluginArtifact`
的第一个生产调用方),全部通过才解包到 `<OS_HOME>/plugins/<env>/<id>/<version>/`,并写安装记录(key
id + 指纹)。
- **为什么改:** #27123 裁决 B′ 的第 1 片(ADR-0025 §3.5 M1 本地 T0 安装闭环)。此前签好名的
`.osplugin` 没有任何命令能安装它。
- **风险与回滚:** 只新增命令、一张部署级表和两个 runtime 存储模块;`os serve` 还不加载已安装的插件(那是第 2
片),所以运行时行为不变。和 #27246 在 `platform-objects`
注册清单及其测试上会有文本冲突,后合的一方取两边并集即可。回滚 = revert 本 PR。
- **先看:**
`packages/cli/src/commands/plugin/install.ts`(拒绝的顺序和名字);`packages/runtime/src/plugin-trusted-keys.ts`(一个
key id
只对应一把钥匙、吊销后不能复活);`packages/platform-objects/src/system/sys-trusted-key.object.ts`。
- **两分钟验证:** `pnpm --filter @objectstack/cli exec vitest run --project
integration src/commands/plugin/install.integration.test.ts`,预期 16
个全绿:信任后安装成功;未知键、已吊销键、键行不可用、签名对不上字节、没有签名、签名格式坏、签名读不了、制品路径读不了、integrity
不符、越界路径、安装记录文件坏、另一个项目占着同一目录,都按名字拒绝;每个用例自建目录和库,顺序无关(打乱顺序跑、单独跑都绿)。
- **需要你做的:** 需要你批准合并:本 PR 改动 3096 行(+3016 / -80,含生成文件),超过 3000 行的人工合并线,按
Tier H 落地,席位在 CI 绿、契约审查通过后来请你批。ADR-0025 §3.5 v2 里 "Steps 4–7 do not
exist" 那句随卡上的 Tier H 文档跟进去改,本 PR 不碰 `docs/adr/**`。
## What changes
**`sys_trusted_key`**
(`packages/platform-objects/src/system/sys-trusted-key.object.ts`) — the
environment's trusted plugin-publisher keys, as ruled: `key_id` (unique
`'global'`), `public_key` (SPKI PEM), `fingerprint` (`sha256:` + hex of
the SPKI DER bytes, unique `'global'`), `source` (`local` now;
`platform` / `cloud` reserved and written by nothing), `status`
(`active` | `revoked`), `added_by`, `added_at`. Deployment-level by
ADR-0131 D7: `systemFields: { tenant: false }`, `requiredPermissions:
['manage_platform_settings']`, `managedBy: 'engine-owned'`, API `get` /
`list` only, so no API caller can add or revive a key. Platform root
keys are not rows.
- **Registration home (ADR-0029 D1 / D2).** `PlatformObjectsPlugin`'s
first manifest, beside `sys_secret`, on the project database. Its
readers are the install loop itself, `os plugin install` now and the
boot-time loader of slice 2, and no capability plugin owns that loop:
the loader runs at boot, before any optional plugin would register the
table, and every one-shot boot the commands take composes
`PlatformObjectsPlugin`. So it is core infrastructure under D2,
registered where its siblings are; no capability plugin's `own` list
changes.
**Runtime stores** (`@objectstack/runtime`, exported because the CLI
writes them and the slice-2 loader is to read the same ones):
- `plugin-trusted-keys.ts` — `readPublisherPublicKey` refuses a PEM
holding a private key (before `createPublicKey`, which would quietly
derive the public half), an unreadable one, and a key that is not
Ed25519, naming its type. `addTrustedKey` writes in system context and
keeps one key id ↔ one key: a different key under a taken id, the same
key under a second id, a revoked id, or a revoked key under a new id are
each refused by name; the same key under the same id again writes
nothing. `revokeTrustedKey` keeps the row (`status: 'revoked'`).
`resolveTrustedKey` answers `active` (with the key) / `revoked` /
`unknown` / `invalid`, the last when the stored fingerprint is not the
stored PEM's.
- `plugin-installation-store.ts` — `pluginInstallDir(env, id, version)`
= `<OS_HOME>/plugins/<env>/<id>/<version>/`, refusing an id or version
that is not one safe path segment. The record (`id`, `version`,
`runtime`, `status`, `grantedPermissions`, `dir`, artifact sha256,
`signature: { keyId, fingerprint } | null`, `trustUnsigned`,
`installedAt`) is kept in
`<OS_HOME>/plugin-installations/<packageStateFileName(env, cwd)>`, the
package disable-state store's own file-name rule (ADR-0016 §9.7),
written by temp file + rename. An unreadable record file is refused,
never rewritten from empty.
**Commands** (`packages/cli/src/commands/plugin/`):
- `os plugin keygen --out PATH [--key-id ID] [--force]` — writes
`PATH.key.pem` (PKCS#8, mode `0600`) and `PATH.pub.pem` (SPKI), prints
the key id (default: the first 16 hex digits of the fingerprint), the
fingerprint, and the `sign` / `trust add` lines. Never overwrites either
file without `--force`. The private key never leaves the developer's
disk.
- `os plugin trust add PUB.pem --key-id ID`, `trust list [--json]`,
`trust revoke ID` — admin writes through a plain one-shot boot with
`PlatformObjectsPlugin` + `AuditPlugin`, so each write lands in
`sys_audit_log` by the audit mirror; `list` takes the read-only boot
(`deferSchemaDdl` + `readOnlyProbe`, absent table read as empty).
- `os plugin install ARTIFACT [--sig PATH] [--trust-unsigned] [--json]`
— in order, every refusal named and exit 1, nothing written before the
last check passes:
1. the archive (`readTarGz`): `artifact_not_found`,
`artifact_unreadable` (also an artifact path that is a directory or
cannot be read), `artifact_entries_refused` (absolute, `..`,
empty-segment, backslash / NUL, non-normal, duplicate or case-folded
duplicate, file-and-directory, or reserved entry paths, the reserved
names in any letter case), `manifest_unreadable`, `manifest_incomplete`,
`manifest_unsafe_identity`;
2. integrity at unpack (`verifyIntegrity`): `integrity_malformed`,
`integrity_mismatch` (one line per violation); an absent map is the
manifest's permissive pass;
3. the signature: `signature_not_found` (an explicit `--sig` that is not
there), `signature_unreadable` (a `.sig` that is a directory or cannot
be read, never taken for an absent one, so `--trust-unsigned` cannot
install around it), `signature_missing`, `signature_malformed`; the key
id resolved in the store through the read-only boot:
`signature_key_untrusted`, `signature_key_revoked`,
`trusted_key_invalid`; then `verifyPluginArtifact` with
`requirePlatform: false`: `signature_invalid` /
`signature_verification_failed`.
4. before anything is written: this project's installation records are
read, and a file it cannot read is refused (`record_unreadable`) rather
than clobbered by the write; an install directory that already holds
other bytes (its `artifact.osplugin` sha256 differs, or it has none)
that no record of this project owns is refused (`install_dir_taken`):
two projects sharing one `OS_HOME` and environment id share
`<OS_HOME>/plugins/<env>/<id>/<version>/` under the ruled layout, and
one must not overwrite the other's install. Installing the very bytes
the directory holds, or replacing a directory this project's record
owns, goes ahead.
5. materialize (staging dir + rename, so a failed write leaves no half
install, and a failed swap puts the earlier install back and removes the
new tree), keeping `artifact.osplugin` and `artifact.osplugin.sig`
beside the files; then the record.
`--trust-unsigned` applies only to an artifact with NO signature: it
installs with a warning on stderr and records `trustUnsigned: true`. A
present signature is always verified; the flag never makes a bad one
pass (pinned). Per triage 6463804227 there is no "unsigned with warning"
arm without the flag. The environment id is `OS_ENVIRONMENT_ID`, else
`env_local`. The human face ends by saying `os serve` does not load
installed plugins yet.
**Self-descriptions slice 1 makes false**, corrected (prose only):
- `packages/spec/src/kernel/manifest.zod.ts` — `PluginIntegritySchema`
TSDoc ("NOT RE-VERIFIED AT UNPACK … there is no `os plugin install`")
and the `integrity` field's TSDoc now say `os plugin install`
re-verifies at unpack. No `.describe()` text changed.
- `packages/core/src/security/index.ts`, `plugin-artifact-integrity.ts`;
`packages/cli/src/utils/osplugin.ts`, `commands/plugin/publish.ts`,
`commands/plugin/sign.ts` — comments naming the "future runtime loader"
as the only re-verifier.
- `packages/cli/README.md` ("The group has no `install`") and
`content/docs/protocol/kernel/lifecycle.mdx` (the top callout, the
*Plugin Installation* callout, *What an author does today*, and the
Summary's *Atomic Installations* line, each of which said there is no
`os plugin install` or that code-plugin installation is not implemented)
— the three commands listed, and `os serve` named as not loading
installed plugins yet.
- `packages/spec/liveness/manifest.json` — the `integrity` row moves
`dead` → `live` with in-repo evidence `install.ts#PluginInstall` and
`publish.ts#PluginPublish`; its note had assigned that flip to whatever
built re-verification at unpack. `liveness/state-counts/manifest.md`
regenerated (`gen:liveness-counts`), and `liveness/README.md`'s manifest
row no longer counts `integrity` among the dead.
- Left as they are, true until slice 2: `PluginRuntimeSchema` "nothing
dispatches on the tier", `plugin-loader.ts`'s format-only signature
check, `plugin-artifact-signature.ts`.
**Changesets:** `.changeset/27123-local-plugin-install.md`
(`@objectstack/cli`, `@objectstack/runtime`,
`@objectstack/platform-objects`, patch) and
`.changeset/27123-integrity-reverified-at-unpack.md`
(`@objectstack/spec`, `@objectstack/core`, patch, prose only); both
carry the two declaration lines.
## Tests
- `packages/runtime/src/plugin-trusted-keys.test.ts` (14) — against a
real `ObjectQL` + `SqlDriver` over a SQLite file holding the real
`sys_trusted_key`, never a double: the PEM refusals (a private key, with
the control that node itself would derive a public key from it; RSA,
naming `'rsa'`; non-PEM); add / idempotent re-add / `key-id-invalid` /
`key-id-taken` / `key-trusted-under-another-id`; revoke keeps the row
and refuses both `key-id-revoked` and `key-revoked` afterwards;
`resolveTrustedKey` hands back a key that verifies what its private half
signed, and answers `invalid` for a row whose fingerprint is another
key's (with the anti-vacuity control that the PEM itself is a good key);
and the table itself refuses a second row of the same key, and a second
key under the same id, written around the store (two adds racing), with
the control that a second key under a second id is a row.
- `packages/runtime/src/plugin-installation-store.test.ts` (10) — the
install path, six unsafe id / version segments refused, round trip and
replace-by-id, the project-keyed file name equal to
`packageStateFileName`, and a corrupt record file refused and left
byte-identical.
- `packages/cli/src/utils/plugin-install.test.ts` (28) — every unsafe
entry path named once; materialize keeps the original bytes and
signature, writes no `.sig` for an unsigned install, and a re-install
replaces the old tree whole with no staging dir left; two paths
differing only in letter case; a file that is also another entry's
directory (three shapes, one case-folded), with a control; the reserved
names in other letter case; `installedArtifactSha`'s three answers; a
swap that fails at either rename leaves the earlier install whole and no
staging dir, and a leftover from an earlier failed swap does not block
the next (each of the three fails against the pre-fix code).
- `packages/cli/test/plugin-keygen.test.ts` (4) — the pair is one
Ed25519 key, private half `0600`, the printed fingerprint is the public
half's, the private key's text never reaches the terminal, the default
key id, `--force`, and an unusable key id writing nothing.
- `packages/cli/src/commands/plugin/install.integration.test.ts` (16,
integration tier) — in process through oclif, real SQLite, every case on
its own `OS_HOME`, project and database, so the cases hold in any order
(green shuffled, `--sequence.shuffle`, and one at a time): an untrusted
key refused and no database file created; `trust add` writes the row and
an audit row, re-add writes nothing, a private key is refused; a signed
install materializes and records the key; `signature_invalid`;
`trusted_key_invalid` (the row's PEM swapped under its fingerprint);
`artifact_unreadable` (an artifact path that is a directory);
`signature_malformed`; `signature_unreadable` (a `.sig` directory, with
and without `--trust-unsigned`); `integrity_mismatch`;
`artifact_entries_refused`; `signature_missing` then `--trust-unsigned`
installing with the stderr warning; `--trust-unsigned` still refusing an
untrusted, a malformed, an other-bytes and a revoked signature;
`signature_key_revoked` after `trust revoke` with the earlier install
left in place; `record_unreadable` leaving the corrupt file
byte-identical; and two projects on one `OS_HOME`: the second is refused
`install_dir_taken` with the first's tree untouched, the owner may
replace, and installing the bytes already there goes ahead.
-
`packages/cli/src/utils/schema-migrate.one-shot-family.integration.test.ts`
— the four new `bootSchemaStack` callers declared in `CALLERS` (the
first case reads the family off the source, so a caller left out of the
table is named there): `plugin install` and `plugin trust list` as
no-write modes (database byte-identical, no SQLite file brought into
existence, one JSON document), `plugin trust add` and `plugin trust
revoke` as write modes (no seed write), all four leaving the key home
empty. A `@FIXTURE@` placeholder carries a signed artifact and public
key written in `beforeAll`, and one added case pins that the install
fixture reaches the store read (`signature_key_untrusted`) rather than
refusing earlier, so its no-write cases cannot hold vacuously.
- Updated: `packages/platform-objects/src/plugin.test.ts` (registration
roster gains `sys_trusted_key`) and
`deployment-level-no-tenant-column.test.ts` (the deployment-level
population gains it).
## Acceptance notes
- **Store transport.** The commands reach `sys_trusted_key` through the
CLI's one-shot `bootSchemaStack` (read-only for `install` / `list`,
plain for `add` / `revoke`), the family the `os migrate` / `os secret`
commands use, against `--database-url` / `OS_DATABASE_URL` or the
project's default database. No HTTP route was added.
- **Slice 2 reads what this writes.** The boot-time loader is to read
the installation record (`loadPluginInstallations`), re-verify
`artifact.osplugin` against `artifact.osplugin.sig` with
`resolveTrustedKey`, and refuse a key revoked since install. Nothing
here loads or dispatches on `runtime`.
- **Key id, not fingerprint, is the lookup.** `install` resolves the
signature's key id, then `resolveTrustedKey` re-derives the fingerprint
from the stored PEM and refuses a mismatch; the record keeps both.
- **Merge collision with #27246** (draft, `sys_exchange_rate`): both PRs
append to `PlatformObjectsPlugin`'s first manifest and to the same
roster assertion in `plugin.test.ts`. Both also move
`scripts/platform-object-tenancy-census.json`. Whichever lands second
takes the union in the two files (five objects, "The five above") and
regenerates the census with `node
scripts/platform-object-tenancy-census.mjs --write`.
- **ADR text.** ADR-0025 v2 §3.5 still says "Steps 4–7 do not exist …
there is no `os plugin install`". It is not edited here (no
`docs/adr/**` edit); it rides the card's Tier H docs follow-up.
- **File surface beyond the commands and the object** (all inside the
slice): `packages/cli/src/utils/organization-ownership-inventory.ts`
(the ADR-0131 D10 inventory row for `sys_trusted_key`: census
`platform-census-main`, fate `column-drop`, as `sys_secret`);
`scripts/platform-object-tenancy-census.json` (derived, regenerated with
`--write`: the new object joins the population, 87 → 88 registered, out
of reach as deployment-level);
`packages/runtime/src/plugin-trusted-keys.ts`,
`plugin-installation-store.ts` and their `index.ts` exports;
`packages/cli/src/utils/plugin-trust-store.ts`, `plugin-install.ts`; the
one-shot family pin; the self-description comment edits in
`packages/core/src/security/*`, `packages/cli/src/utils/osplugin.ts`,
`publish.ts`, `sign.ts`, `packages/cli/README.md`,
`content/docs/protocol/kernel/lifecycle.mdx`;
`packages/spec/src/system/constants/platform-object-names.ts` (the
registry row) and `packages/spec/liveness/` (`manifest.json`,
`state-counts/manifest.md`, `README.md`).
- **Size: Tier H.** 3096 changed lines (+3016 / -80, generated included)
against the 3000 human-merge threshold, measured by dispatch-gates on
`6b3a8286`; review round 1 added about 400, mostly tests (integration 10
→ 16, unit 17 → 28). It lands on an authorized APPROVED review or a
human merge.
- **Metadata-protocol: none, measured.** `sys_trusted_key` is an
engine-owned system object with a read-only API; no metadata type,
authorable key or accepted value is added (`check:authorable-surface`
and `check:api-surface` green, the spec's 14 generated artifacts current
with nothing to regenerate).
- **Premises 1–3 of 6464984121, re-run on `origin/main` `9ff8154c`:**
`verifyPluginArtifact(` had one hit, its definition
(`plugin-artifact-signature.ts:264`); on this branch the second is
`install.ts`. `sign.ts` reads `--key` / `--key-id` and writes only the
signature. `allowedSigners` outside `packages/spec`: 0 hits.
## Known limitations
- **The installations file's read-modify-write takes no lock.** Two `os
plugin install` runs for one project and environment at the same moment
can each read the file, and the second rename wins, dropping the first's
record (its tree stays on disk). The write itself is atomic (temp file +
rename), so no reader sees half a file. A lock is out of this slice.
- **gunzip has no size cap before the signature check.** `readTarGz`
inflates the whole artifact before its signature is verified, so a
hostile `.osplugin` can make the command spend memory before it is
refused. The signature covers the compressed bytes, so a cap belongs on
the inflate, not after it.
- **Two projects that installed identical bytes share ownership.**
`install_dir_taken` refuses bytes no record of this project owns; when
both projects' records name the same sha, either may later replace the
tree, and the other's record then names a sha the directory no longer
holds. The slice-2 loader is to check the kept artifact's sha256 against
the record before it trusts the tree, which refuses that case at boot.
## Verification
Review round 1, at head `ae1b0536` (the CI red:
`PLATFORM_OBJECTS_BY_PACKAGE['platform-objects']` lacked
`sys_trusted_key`, reproduced locally and fixed), each inside
`scripts/pm/os-verify-lock.sh`, every VERDICT `command-exit 0`:
- runtime `plugin-trusted-keys` + `plugin-installation-store` — Test
Files 2/2, Tests 24/24.
- `pnpm --filter @objectstack/platform-objects exec vitest run` — Test
Files 97/97, Tests 1369/1369.
- cli unit `plugin-install` + `plugin-keygen` + exit-signal pin — Test
Files 3/3, Tests 154/154.
- cli integration `install.integration` + one-shot family pin — Test
Files 2/2, Tests 140/140.
- `pnpm --filter @objectstack/spec run test:repo` — Test Files 84/84,
Tests 1396/1396.
- cli `tsc --noEmit` clean; eslint `--no-inline-config` over the touched
files exit 0; `check:liveness` exit 0.
- `node scripts/pm/dispatch-gates.mjs --ran …` — 119 derived, 119 run, 0
NOT-MEASURED, 0 UNRUN; every one exited 0.
First round, at head `70cdda4e`:
All at head `70cdda4e` (origin/main `ebfadb02` merged in), each inside
`scripts/pm/os-verify-lock.sh`, every VERDICT `command-exit 0`:
- `pnpm --filter @objectstack/runtime exec vitest run
src/plugin-trusted-keys.test.ts src/plugin-installation-store.test.ts` —
Test Files 2/2, Tests 23/23.
- `pnpm --filter @objectstack/platform-objects exec vitest run` — Test
Files 97/97, Tests 1369/1369.
- `pnpm --filter @objectstack/cli exec tsc --noEmit -p tsconfig.json` —
no errors.
- `pnpm --filter @objectstack/cli exec vitest run
src/utils/plugin-install.test.ts test/plugin-keygen.test.ts
test/exit-signal.pin.test.ts` — Test Files 3/3, Tests 143/143 (the
exit-signal pin discovers the four new commands on its own).
- `pnpm --filter @objectstack/cli exec vitest run --project integration
src/commands/plugin/install.integration.test.ts
src/utils/schema-migrate.one-shot-family.integration.test.ts` — Test
Files 2/2, Tests 134/134.
- Before the merge, at `a0c22f13`: `pnpm --filter @objectstack/runtime
exec vitest run` — Test Files 473/473, Tests 8598 passed, 19 skipped.
- `pnpm --filter @objectstack/spec check:generated --fix` — "All 14
generated artifacts are up to date", nothing regenerated (the spec edit
is TSDoc only).
- eslint (`--no-inline-config`) over `packages/cli packages/runtime
packages/platform-objects packages/core packages/spec/src/kernel` — exit
0.
- `node scripts/pm/dispatch-gates.mjs --ran … --repo
objectstack-ai/objectstack` — "117 derived famil(ies) accounted for —
117 run, 0 NOT-MEASURED". Every one of the 117 exited 0. 104 of them ran
on the pre-merge tree; the five that first exited non-zero were re-run
on `70cdda4e`, together with the eight the merged tree adds:
- `pnpm check:platform-object-tenancy-census` — exit 1 on the first run
(`sys_trusted_key` new to the population), then 0 after `--write`;
- `check:skill-examples`, `check:dual-build-cjs-loads`,
`check:i18n-coverage` — exit 3 (no build output) on the first run, then
0 after a full `turbo run build`;
- `check-plugin-teardown-shape --self-test` — exit 3 (a shallow clone
without its pinned fixture commit), then 0 after fetching it.
- Manual run of the loop by hand (isolated `OS_HOME`, `--database-url
file:…`): keygen → install refused untrusted (no `app.db` created) →
trust add (private key refused; idempotent) → install → tamper /
unsigned / malformed / escape refusals → `--trust-unsigned` → revoke →
install refused revoked → trust add of the revoked key refused.
`sys_audit_log` carried the `create` and `update` rows for the key.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Utp6CXg4K8dxetvqvU81bD)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent b8f4acc commit 10f27b6
37 files changed
Lines changed: 3016 additions & 80 deletions
File tree
- .changeset
- content/docs/protocol/kernel
- packages
- cli
- src
- commands/plugin
- trust
- utils
- test
- core/src/security
- platform-objects/src
- system
- runtime/src
- spec
- liveness
- state-counts
- src
- kernel
- system/constants
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
| |||
195 | 196 | | |
196 | 197 | | |
197 | 198 | | |
198 | | - | |
| 199 | + | |
199 | 200 | | |
200 | | - | |
201 | | - | |
202 | | - | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
203 | 208 | | |
204 | 209 | | |
205 | 210 | | |
| |||
369 | 374 | | |
370 | 375 | | |
371 | 376 | | |
372 | | - | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
373 | 380 | | |
374 | 381 | | |
375 | 382 | | |
| |||
381 | 388 | | |
382 | 389 | | |
383 | 390 | | |
384 | | - | |
385 | | - | |
386 | | - | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
387 | 395 | | |
388 | 396 | | |
389 | 397 | | |
| |||
912 | 920 | | |
913 | 921 | | |
914 | 922 | | |
915 | | - | |
| 923 | + | |
916 | 924 | | |
917 | 925 | | |
918 | 926 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
142 | 142 | | |
143 | 143 | | |
144 | 144 | | |
145 | | - | |
| 145 | + | |
146 | 146 | | |
147 | 147 | | |
148 | 148 | | |
149 | 149 | | |
150 | 150 | | |
151 | 151 | | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
152 | 155 | | |
153 | | - | |
| 156 | + | |
154 | 157 | | |
155 | 158 | | |
156 | 159 | | |
| |||
0 commit comments