Commit 434c6c7
Fixes #21220
Clause-②: yes
Dispatched by the PM claim `5938507454` (PM loop round 1, `domain:spec`
seat 1), on the triage direction `5938409101`. An ADR-0021 dataset's
`dimensions[].field` and `measures[].field` now take the
column-reference accept set the cube members they compile to already
hold since #20943 (PR #20998), from ONE shared declaration. A non-column
value is refused at parse, at `dimensions.N.field` / `measures.N.field`,
with a prescription naming the ADR-0021 form. The runtime door from PR
#21190 is untouched and stays as defence in depth. The changeset carries
the `(narrowing)` arm, the BREAKING banner at `minor`, and the ADR-0087
marker `registered dataset-member-field-expression-refused,
dataset-count-measure-empty-field-removed`.
## Patch round 2 — contract review `5940617829`, item ①.6
The review found one lossless sub-shape that the first round sent to D3
only: a dataset measure `{ aggregate: 'count', field: '' }`. It parses
on the base, and the #21190 door skips it (its `!== ''` guard). On
SQLite's native path it compiled to `COUNT()` and answered 200. Its
producer is Studio's dataset inspector. This round:
- **New D2 conversion `dataset-count-measure-empty-field-removed`** in
`MAJOR_18_CONVERSIONS` (`order: 54`, inserted at its identifier's sort
position, defined directly above `elementFilterRemoved`). `main` landed
`form-field-public-picker-removed` at 53, so this entry takes the next
free number.
- It drops `field` from a `count` measure whose `field` is exactly `''`.
Without the key, `compileDataset` emits `sql: m.field ?? '*'`, which is
`COUNT(*)`: the row count.
- Its mechanics are measured from the precedent
`time-default-utc-suffix-dropped`, not recalled: `toMajor: 18`,
`retiredFromLoadPath: true` (ADR-0087's ratified pre-GA policy for a
lossless repair), and `retiredAfter: '17.5.0'` (the spec's current
version, the same value the precedent carries).
- It uses the shared `stripKeys` helper and emits one notice per removed
key.
- Its fixture is disjoint. The controls are left as stored: a dimension
`''`, a `sum` over `''`, a count over `'*'`, a count with no `field`,
and a count over a column.
- **Scope: only `count` + `''`.** A non-count measure with `''`, a
dimension with `''` and every expression have no working row or no
mechanical rewrite, so they stay D3-only. A padded value has no known
producer and is out of scope.
- **The D3 entry links the conversion** with `conversionIds:
['dataset-count-measure-empty-field-removed']`, the way
`18.time-default-zone-refused.ts` links its conversion. Its `reason` now
says what is true: the door refuses an expression, and it never judged
`''`. It also says that D2 carries the `count` + `''` repair and D3 the
rest. `acceptanceCriteria`, the `STEP18_RATIONALE` fragment, the
changeset, the two ledger notes and the `dataset.zod.ts` comment are
corrected to match. `registry.ts` was regenerated by
`gen:migration-registry`.
- **Pins.** The new
`src/conversions/dataset-count-measure-empty-field-removed.test.ts`
covers four things on a STORED row, through
`applyConversionsToStoredItem('dataset', row)`:
- The key is dropped, with one notice per measure, and the row then
parses.
- Every control is the same reference.
- The replay is idempotent.
- The conversion is registered under major 18, retired from the load
path, and linked from the D3 entry.
The table-wide fixture replay in `conversions.test.ts` covers before →
after.
- **What a NEW save does.** The write path parses with the current
schema and replays no conversion, so a new Studio save of `field: ''` is
still refused at save with the prescription to omit the key. The
producer-side change stays objectui's. A row already stored that way is
repaired on load at every stored-row seam.
## What changes
**`@objectstack/spec`**
- **One pattern.** The new module
`packages/spec/src/data/analytics-column-reference.ts` declares the
column path once (a bare identifier, then zero or more `.identifier`
hops). It sits outside the `data` barrel, like
`ui/analytics-carrier-filter.ts`, so it is not published API. It exports
two anchored forms built from that one source string:
- `ANALYTICS_COLUMN_REFERENCE`: the path, or `'*'`.
- `ANALYTICS_COLUMN_PATH`: the same path without the `'*'` arm.
- **Cube layer unchanged.** In `data/analytics.zod.ts`,
`CUBE_MEMBER_SQL` is now that same `RegExp` object: `const
CUBE_MEMBER_SQL = ANALYTICS_COLUMN_REFERENCE`. The declaration stays in
this file on purpose. ADR-0021's 2026-10-01 note links to
`analytics.zod.ts#CUBE_MEMBER_SQL`, and ADRs are a governed surface this
PR does not edit. The cube members' JSON-Schema `pattern` is
byte-identical; the new pin asserts it.
- **Dataset layer.** In `ui/dataset.zod.ts`:
- `DatasetMeasureSchema.field` uses
`.regex(ANALYTICS_COLUMN_REFERENCE)`. Admitted: a column, a relationship
path, or `'*'`. A count may still omit `field`.
- `DatasetDimensionSchema.field` uses `.regex(ANALYTICS_COLUMN_PATH)`,
so a dimension also refuses `'*'` (see measurement 3).
- Both are `.regex()`, not refinements, so the published JSON Schema
carries each as a `pattern`. `dropped-refinements.baseline.json` is
untouched.
- The refusal code is `invalid_format`. Each prescription opens with the
contract sentence and names ADR-0021.
- The measure prescription names the measure `filter` form and `derived:
{ op, of: [...] }`, with the 0–1 ratio scale.
- The dimension prescription says a CASE bucket becomes a field of the
object.
- The two `describe()` texts now say "never a SQL expression".
`content/docs/references/ui/dataset.mdx` is regenerated from them.
- **ADR-0087.**
- New D3 entry
`migrations/entries/semantic/18.dataset-member-field-expression-refused.ts`.
`registry.ts` was regenerated by `gen:migration-registry` and never
hand-edited inside the markers.
- One hand-written `STEP18_RATIONALE` fragment, inserted at the id's
sort position with `order: 58`. Round 1 used 57. After the round-2 base
merge it takes 58, because 57 is allocated to the in-flight PR #21244's
fragment. Neither list requires unique orders:
`step18-rationale-merge.test.ts` models two fragments sharing one
`order` and only asserts a positive integer, and `main` already holds
two 56s. So whichever of the two PRs lands first, neither re-orders.
- One D2 conversion, `dataset-count-measure-empty-field-removed`, for
the one lossless sub-shape (a `count` measure's `field: ''`; see Patch
round 2). The D3 entry carries the rest: an expression has no mechanical
rewrite into a column.
- No `RETIRED_KEYS_BY_MAJOR` row: no key left the shape.
- **Liveness.** The `dataset` ledger rows `dimensions.field` and
`measures.field` stay `live`. Each is re-verified on 2026-10-01, with
the narrowing recorded in its `note`.
- **Guide.** `content/docs/data-modeling/analytics.mdx` gains one "Key
rules" bullet saying that `field` is a column reference.
**Ratchets, as expected for a value narrowing.** The `api-surface`,
`authorable-surface`, `json-schema.manifest`, `export-origins` and
`declaration-map` artifacts are byte-identical. `spec-changes.json` and
the upgrade guide stay at protocol 17, so major-18 entries do not
project yet, and both checks are green.
## The PM's mechanism assumptions, measured
1. **Confirmed.** `dataset.zod.ts:125` (dimension, required) and `:189`
(measure, optional) were bare `z.string()` at the base `d6d6e872`.
`CUBE_MEMBER_SQL` was a module-private `const` at
`analytics.zod.ts:240`.
2. **Exporting `CUBE_MEMBER_SQL` would move the public surface.**
`packages/spec/src/data/index.ts` re-exports the whole module with
`export * from './analytics.zod'`, so an exported `CUBE_MEMBER_SQL`
becomes a new `@objectstack/spec/data` export and needs
`gen:api-surface`. I took the non-public module instead.
`check:api-surface`, `check:export-origins` and `check:declaration-map`
are green with zero changes to their artifacts.
3. **`'*'` on a dimension: measured, and refused.** Readings come from
`POST /api/v1/analytics/dataset/query` with today's spec, through the
real REST route, a real `AnalyticsService` and a real better-sqlite3
`SqlDriver`, using a temporary probe test that was deleted afterwards
(the tree is clean). The ObjectQL-strategy column bridges
`executeAggregate` straight to `SqlDriver.aggregate`, not through the
ObjectQL engine.
| dataset member `field` | native-SQL strategy | ObjectQL strategy |
|---|---|---|
| dimension `'*'` | 500 `DATABASE_ERROR` (`SELECT * AS ... GROUP BY *`)
| 500 `DATABASE_ERROR` (`groupBy: ['*']`) |
| dimension `''` | 500 | 500 |
| count measure `''` | 200 (SQLite accepts the `COUNT()` it compiled to)
| 500 |
| count measure `'*'` (control) | 200 | 200 |
| sum measure `'*'` | 500 (`SUM(*)`) | 500 |
| padded `' amount'` (sum) | 200 | 200 |
| padded dimension `' industry'` | 200 | 200, **dimension column
silently missing from the rows** |
| expression `amount * 2` | 403 `PERMISSION_DENIED` (PR #21190's door) |
403 |
A `'*'` dimension is never answered: it compiles to grouping by every
column, which is not an axis. So the dataset dimension takes the same
path pattern without the `'*'` arm. That is one pattern source with one
stated restriction, not a second pattern; the pin proves the dimension's
published `pattern` equals the cube's with only the `\*|` arm removed.
⚠ **Flagged, not silently chosen.** The triage line reads "exactly the
`CUBE_MEMBER_SQL` accept set" for both keys. This PR narrows the
dimension one step further, as the dispatch's mechanism item 3 invited
and the card's own pin wording ("`*` (on a measure)") suggests. The cube
`DimensionSchema.sql` still admits `'*'`, per ruling D's execution
parameters, and is untouched here.
4. **Census, repo-wide, with a lit control.** A scan of every `field:`
value in tracked files that mention a dataset and
`dimensions`/`measures`, including `packages/**` tests,
`content/docs/**` and `skills/**`.
- **Lit control.** Column-reference values hit in every area, and the
scanner sees them: `examples` 116, `content` 88, `skills` 15,
`platform-objects` 6, `service-analytics` 587, `spec` 423, `lint` 282,
`rest` 92.
- **Non-column dataset `field`s found:** only the fixtures that PR
#21190 wrote on purpose to drive its door: `rest`
`analytics-16019-driver-declared-fault.test.ts` (`translate(...)`,
`lower(name)`) and `service-analytics`
`inline-dataset-field-admission-door.test.ts` (an expression constant
and a template). Both were re-pinned (next section).
- **Zero** in the examples, `platform-objects`, the hand-written docs
and the published skills. Every other non-column literal the scan caught
is not a dataset field (driver-sql and protocol prose, filter paths,
`$field` prose in a skill).
- **The build as judge.** The full suites of `spec`, `lint`,
`service-analytics`, `metadata-protocol` and `rest` are green on the
narrowed contract.
- Nothing in `skills/**` teaches an expression `field`, so no Tier H
follow-up is owed.
5. **D2 for one sub-shape, D3 for the rest** (corrected in round 2; the
first round said "D3 only").
- **Expressions: D3 only.** A stored dataset with an expression `field`
already answered 403 at the dataset door. On the REST route it is now
refused one step earlier, at the route's own `DatasetSchema` parse,
which the route runs on the inline and the saved branch alike. An
expression has no mechanical rewrite.
- **Correction.** This item said that no stored row worked before. That
is false by this PR's own probe table: a `count` measure with `field:
''` answered 200 on SQLite's native path, and the door never judged it.
- **The repair.** That sub-shape gets the D2 conversion
`dataset-count-measure-empty-field-removed`, which every stored-row
rehydration seam replays (`applyConversionsToStoredItem`, e.g.
`metadata-protocol`'s `convertStoredItemDetailed`). The runtime door is
still reachable for a dataset handed to `queryDataset` unparsed: the
build probe's dashboard-widget path (`metadata-protocol`
`build-probes.ts`) passes the stored row as read.
## Fixture triage (two consumer tests the narrowing turns red; both
re-pinned, not loosened)
- **`service-analytics` `inline-dataset-field-admission-door.test.ts`**
built its expression fixtures with `DatasetSchema.parse`, which now
refuses them. The fixtures are now built UNPARSED, the shape a
pre-narrowing stored row has, through `storedDatasetWith`. The controls
still parse. One new case asserts that the contract refuses both
fixtures at `dimensions.0.field` / `measures.0.field`. All 4 provider
tiers x 2 strategies of the 403 door pins are unchanged and green.
- **`rest` `analytics-16019-driver-declared-fault.test.ts`.** The route
parses every dataset first, so its inline and saved expression cases now
answer `400 VALIDATION_FAILED`, where they answered `403
PERMISSION_DENIED`.
- Both cases are re-pinned to the `400`, plus `invalid_format` at the
path inside `detail`, the driver never called, and no expression text
echoed.
- The statement-leak check now reads SQL keywords as the strategies emit
them (upper case), because the prescription itself says "Group by the
column itself" in prose. It was case-insensitive before, when the body
carried no prose.
- The docblocks state the new layering and the reverse-verification
direction (measured below).
## Tests
All runs are at head `0d5e446e` (after merging `origin/main` at
`3ddd3d0c`) unless stated otherwise. Filter direction: each package's
own suite, no consumer sweep.
- **`@objectstack/spec`**
- `vitest run --project local`: 597 files, 17468 passed, 1 todo.
- The new `src/ui/dataset-field-column-reference.test.ts` has 11 cases.
- The cube precedent pin `cube-member-sql-column-reference.test.ts`
stays green, with its `'*'`-on-a-cube-dimension case unchanged.
- **`@objectstack/service-analytics`** `vitest run`: 162 files, 3741
passed, 45 skipped.
- **`@objectstack/lint`** `vitest run`: 119 files, 5502 passed.
- **`@objectstack/metadata-protocol`** `vitest run`: 200 files passed, 3
skipped; 2973 tests passed, 19 skipped.
- **`@objectstack/rest`** `vitest run --project local`: 257 files, 4858
passed, 316 skipped.
- **Typecheck:** `pnpm --filter PKG typecheck` exit 0 for
`@objectstack/spec` (`tsc` + `check:scripts-typecheck` +
`check:test-typecheck`), `@objectstack/service-analytics` (its
`tsconfig` includes all of `src`, so the edited `__tests__` file is in
the program) and `@objectstack/rest` (`tsc` + `check:test-typecheck`).
- **`@objectstack/spec` `--project repo`, the relevant files:**
`step18-rationale-merge`, `conversions-major18-merge`,
`liveness/evidence`, `liveness/proof-registry`,
`retired-key-migrate-sentence`, `file-description`, `root-index`,
`export-list`, `category-title`, `schema-tree-freshness`, `escape-mdx`
and `references-banner`. 12 files, 294 passed.
- **Lint, a declared narrowing.** `eslint --no-inline-config --format
json` over the 8 changed lintable files at `0d5e446e` gave 8 files, 0
errors, 0 warnings.
- **Population:** from `eslint.config.mjs`, the
`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` block. The other changed files
are `.md`, `.mdx` and `.json`.
- **File count:** read from the JSON output.
- **Invariance:** the config never enables type-aware linting (no
`parserOptions.project`, no typed rules), so this diff cannot move a
verdict on an untouched file.
- The whole-repo `pnpm lint` is CI's.
## Round 2 readings (final head `fc4e91c0`; `origin/main` `3dc33b2d`
merged through `os-regen-merge.sh`)
These readings were taken after a container restart. The restart cut a
first round-2 gate run short at `2e57fa29`. Every reading below was
re-taken at `fc4e91c0`, the pushed head.
- **Build.** `turbo run build` over the closures of spec, cli,
service-automation, metadata-protocol, rest and client-react: 59/59
tasks.
- **`@objectstack/spec`**
- `vitest run --project local`: 597 files, 17465 passed, 1 todo. The
counts moved with `main`'s merge.
- That includes the new
`src/conversions/dataset-count-measure-empty-field-removed.test.ts`, the
table-wide fixture replay in `conversions.test.ts` and
`retired-after.census.test.ts`.
- `--project repo`: the same 12 relevant files as round 1
(`step18-rationale-merge` and `conversions-major18-merge` among them),
294 passed.
- **Consumers that read the conversion table.**
- `@objectstack/cli` `meta.report-order.test.ts` (unit tier): 16 passed.
- `@objectstack/service-automation`
`decision-overlapping-edge-conditions.pin.test.ts`: 22 passed.
- `@objectstack/metadata-protocol` full suite (it hosts the stored-row
seam): 200 files passed and 3 skipped; 2973 tests passed and 19 skipped.
- **Not re-run this round.** `rest`, `service-analytics` and `lint`:
this round's diff does not reach them (spec only), and their round-1
readings stand.
- **Lint, a declared narrowing.** `eslint --no-inline-config --format
json` over the 10 changed lintable files at `fc4e91c0` gave 10 files, 0
errors, 0 warnings. The population and invariance are as in round 1.
- **Gates.** `dispatch-gates --commands` at `fc4e91c0` derived the same
115 families. All were run with exit codes recorded, and `--ran`
reconciled them as "115 derived, 115 run, 0 NOT-MEASURED, 0 UNRUN".
- `check:generated`: 15/15 up to date.
- `check:migration-registry`: exit 0.
- `check:adr-0087-registration`: it reads `registered
dataset-member-field-expression-refused,
dataset-count-measure-empty-field-removed`, both new here.
- `check:skill-examples` and `check:dual-build-cjs-loads` both exited 0
this time. Both had been NOT MEASURED in round 1 for want of built
packages.
## Ablations
Each ablation ran from committed state, disk-verified through
`scripts/ablation-replace.mjs`. Each restore was proven by blob hash
equal to HEAD, an empty `git diff HEAD`, and a clean status. The
predicted direction was "turns red" in all four, and that is what was
observed.
| leg | mutation | under mutation | restored |
|---|---|---|---|
| A1 | measure `field` pattern admits anything | new pin: 7 failed / 4
passed (every measure refusal, door, pattern and defineStack case red;
the dimension and accept cases green) | 11 / 11 |
| A2 | dimension takes `ANALYTICS_COLUMN_REFERENCE` (admits `'*'`) | 3
failed / 8 passed (the dimension refusal case on `'*'` and the two
pattern pins) | 11 / 11 |
| A3 | `ANALYTICS_COLUMN_PATH` admits anything, then `@objectstack/spec`
rebuilt | `ablation-dist-preflight`: marker in 18 built files. `rest`:
the 2 re-pinned cases red, `expected 403 to be 400` (the route's parse
passes the expression to the service door, the direction its docblock
predicts); 6 green. `service-analytics` door test: the contract case
red, 20 green | rebuilt; marker absent from all 230 built files; tree
clean |
| A4 (round 2, at `fc4e91c0`) | the new conversion matches nothing (its
`field !== ''` guard reads a value no row carries) | 2 failed / 239
passed: the `conversions.test.ts` fixture pin
`dataset-count-measure-empty-field-removed: before → after, emits 2
notice(s)` and the stored-row pin are red; the controls are green | blob
`75f4166c` == HEAD; `git diff HEAD` empty; status clean |
No ablation file is left in the tree.
## Gates
`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands`, run at `0d5e446e` with no paths, derived 115 families.
- All 115 were run, each with its exit code recorded before any pipe.
- `--ran` reconciled them: "115 derived, 114 run, 1 NOT-MEASURED, 0
UNRUN", every row carrying its recorded exit code.
- **NOT MEASURED: `check:dual-build-cjs-loads`.** Reason: it exited 3
(`PREREQUISITE NOT MET`) because 44 packages outside this diff's build
closure have no `dist/`, and only a full monorepo build supplies them.
This diff changes no package entry, export map or build config. CI runs
it on the full build.
- **`check:skill-examples`** first exited 3 for want of a built
`@objectstack/client-react`. After building that package it exited 0 at
`0d5e446e`: 259 examples type-check.
- Every other family exited 0. That includes:
- `check:generated`: all 15 artifacts up to date against a stamp-matched
dist.
- `check:adr-0087-registration`: at the first round's head it read
`registered dataset-member-field-expression-refused (new here)`.
- `check:changeset-no-major` and `check:empty-changeset`.
- `check:liveness`, `check:migration-registry` and
`check:doc-authoring`.
- `check:cross-package-test-inputs` and `check:nul-bytes`.
## Acceptance notes
- **File surface, declared.** The claim named `dataset.zod.ts`,
`analytics.zod.ts` "only as far as sharing needs", the ADR-0087 entry
and registry, the retirement kit, pins and one changeset. Four paths go
beyond that, each for the stated reason:
- The new non-public module `data/analytics-column-reference.ts`: the
sharing change itself, which avoids a public export.
- The two consumer test files the narrowing turned red: fixture triage,
re-pinned rather than loosened.
- One bullet in `content/docs/data-modeling/analytics.mdx`: the skill's
docs row.
- **The REST door's answer moves from 403 to 400 for an expression
`field`.** The route's existing `DatasetSchema.parse` refuses it first,
as `VALIDATION_FAILED` naming the path. The changeset says so. The
service door's 403 is unchanged.
- **Studio producer (objectui, outside this repo).**
- **What happens.** `DatasetDefaultInspector.tsx` at the pinned
`31971ff1e` seeds a new dimension row as `{ name: '', field: '', type:
'string' }` and a new measure row as `{ name: '', aggregate: 'sum',
field: '' }`. A plain count measure left with a blank Field box is saved
as `field: ''`. That parsed before. Its query answered 500 on the
ObjectQL path (the SQLite native path happened to accept `COUNT()`).
- **After this PR** a new save of that shape is refused at
`measures.N.field`, with the prescription to omit the key. A row already
stored that way is repaired on load by the D2 conversion
`dataset-count-measure-empty-field-removed`.
- **The producer half** is to omit `field` when the box is blank. It is
reported to the seat, not edited here.
- **Measured, not acted on.** Neither item is filed from here; both are
in the report.
- A `sum` (or any non-count aggregate) over `'*'` parses on a dataset
measure and answers 500 on both strategies. That is the count-only `'*'`
boundary the `analytics_cube` ledger already assigns to #21000's family.
- A padded dimension `field` answered 200 with the dimension column
missing on the ObjectQL bridge. It is now refused at parse. A stored
padded row would still reach that path through the build probe; no
producer of one is known.
Authored by `session_01UtnxvdiN376GF3sgXwAw4d` (rounds 1 and 2).
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent a23be74 commit 434c6c7
14 files changed
Lines changed: 949 additions & 38 deletions
File tree
- .changeset
- content/docs
- data-modeling
- references/ui
- packages
- rest/src
- services/service-analytics/src/__tests__
- spec
- liveness
- src
- conversions
- data
- migrations
- entries/semantic
- ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
81 | 81 | | |
82 | 82 | | |
83 | 83 | | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
84 | 89 | | |
85 | 90 | | |
86 | 91 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
75 | | - | |
| 75 | + | |
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
86 | | - | |
| 86 | + | |
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
| |||
108 | 108 | | |
109 | 109 | | |
110 | 110 | | |
111 | | - | |
| 111 | + | |
112 | 112 | | |
113 | 113 | | |
114 | 114 | | |
| |||
124 | 124 | | |
125 | 125 | | |
126 | 126 | | |
127 | | - | |
| 127 | + | |
128 | 128 | | |
129 | 129 | | |
130 | 130 | | |
| |||
Lines changed: 41 additions & 18 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
34 | 45 | | |
35 | 46 | | |
36 | 47 | | |
| |||
51 | 62 | | |
52 | 63 | | |
53 | 64 | | |
54 | | - | |
55 | | - | |
56 | | - | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
57 | 70 | | |
58 | 71 | | |
59 | 72 | | |
| |||
222 | 235 | | |
223 | 236 | | |
224 | 237 | | |
225 | | - | |
| 238 | + | |
| 239 | + | |
226 | 240 | | |
227 | 241 | | |
228 | 242 | | |
229 | | - | |
230 | | - | |
231 | | - | |
232 | | - | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
233 | 252 | | |
234 | | - | |
235 | | - | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
236 | 257 | | |
237 | 258 | | |
238 | | - | |
| 259 | + | |
239 | 260 | | |
240 | 261 | | |
241 | 262 | | |
| |||
248 | 269 | | |
249 | 270 | | |
250 | 271 | | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
255 | 277 | | |
256 | 278 | | |
257 | 279 | | |
| |||
261 | 283 | | |
262 | 284 | | |
263 | 285 | | |
264 | | - | |
265 | | - | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
266 | 290 | | |
267 | 291 | | |
268 | | - | |
269 | 292 | | |
270 | 293 | | |
271 | 294 | | |
| |||
0 commit comments