Repository navigation
Commit 7dc45eb
Fixes #20316
Clause-②: no (narrowing)
A flow node config its executor cannot run is now refused where the flow
is built, at all three build doors (`FlowSchema.parse`,
`AutomationEngine.registerFlow`, `objectstack validate`), by ONE judge:
`flowNodeConfigRefusals` in `@objectstack/spec/automation`. It covers
the whole family; #20317 is folded into this card:
- **a key a builtin's executor contract requires, left out** — the
census below, `loop.collection` (#20317) and `map.collection` among
them;
- **a `decision` branch list its executor cannot read** — a branch with
no `label` (the card's shape), a branch that is not an object, and
`conditions` that is not an array.
## What was wrong, measured on `origin/main` `a88a1bb399` before the
change
Probe scripts drive door 1 (`FlowSchema.safeParse`, spec `dist`), door 2
(`AutomationEngine.registerFlow` with `installBuiltinNodes`, then
`execute`), and door 3 (the BUILT CLI, `node packages/cli/bin/run.js
validate --json` in a stack directory holding one
`objectstack.config.ts`). The run leg swaps the two marker nodes `y` /
`x` for a recording executor, so the route taken is observable. The
decision has out-edges `d → y` labelled `yes` and `d → x` with
`isDefault: true`.
| lit control | door 1 | door 2 | door 3 | run |
|:--|:--|:--|:--|:--|
| (a) decision branch `{ expression: 'true' }`, no `label` |
`success=true` | registered | `valid: true`, exit 0 | `success=true`,
visited `["x","y"]`: EVERY out-edge |
| (a) control: `{ label: 'yes', expression: 'true' }` | `success=true` |
registered | `valid: true`, exit 0 | `success=true`, visited `["y"]` |
| (b) `conditions: ['true']` | `success=true` | registered | `valid:
true`, exit 0 | `success=false`, `condition evaluation error: A
structural condition …` |
| (c) `loop` with `iteratorVariable` and a one-node body, no
`collection` | `success=true` | registered | `valid: true`, exit 0 |
`success=false`, `loop 'l': config does not satisfy the loop contract —
config.collection: Invalid input` |
| (c) control: the same loop with `collection: [1, 2]` | `success=true`
| registered | `valid: true`, exit 0 | `success=true`, the body ran
twice |
| (d) `map` with `flowName`, no `collection` | `success=true` |
registered | `valid: true`, exit 0 | `success=false`, `map 'm': config
does not satisfy the map contract — config.collection: Invalid input` |
Further shapes measured on the run leg only: `conditions: {}` and
`conditions: 5` fail with `conditions is not iterable`; `conditions:
'x'` fails with a condition evaluation error, because a string is
iterated character by character; a `null` branch fails with `Cannot read
properties of null`; `label: null`, `''`, `0`, `' '` and `42` all run
green down both out-edges. The last two also log the engine's
unclaimed-label warn. `conditions: null` routes like no `conditions` at
all, which is legal and stays admitted.
## After, measured on the merged branch (spec `dist` built from this
head; the CLI reads it at run time)
| lit control | door 1 | door 2 | door 3 |
|:--|:--|:--|:--|
| (a) no `label` | `custom` at `nodes.1.config.conditions.0.label` |
throws the same issue | `valid: false`, exit 1, `custom` at
`flows.0.nodes.1.config.conditions.0.label` |
| (a) control | `success=true` | registered, run visits `["y"]` |
`valid: true`, exit 0 |
| (b) `conditions: ['true']` | `custom` at `nodes.1.config.conditions.0`
| throws the same issue | `valid: false`, exit 1, same path |
| (c) loop, no `collection` | `custom` at `nodes.1.config.collection` |
throws the same issue | `valid: false`, exit 1, same path |
| (c) control | `success=true` | registered, body ran twice | `valid:
true`, exit 0 |
| (d) map, no `collection` | `custom` at `nodes.1.config.collection` |
throws the same issue | `valid: false`, exit 1, same path |
## The fix: one judge, three doors
- `packages/spec/src/automation/flow-node-config-refusals.ts` (new). It
lives beside the walk file, not inside it, because it reads the executor
contracts, and two of their modules import
`flow-node-expression-paths.ts`. A static import there closed a cycle
that read `LEDGER_DECLARED_NODE_CONFIG_SCHEMAS` mid-evaluation
(measured: `z.toJSONSchema(undefined)` in 4 spec tests).
- `getBuiltinNodeConfigContracts()` maps a node type to the very schema
its executor hands `parseNodeConfig`, plus `loop`'s parse condition (a
legacy flat-graph `loop` with no `body` is not parsed, so it stays
exempt). It is built on first use, never at module load.
- `flowNodeConfigRefusals(nodeType, config)` has two arms:
- **Contract arm.** It parses `config ?? {}` against the contract, on
the executor's own condition, and keeps an issue ONLY where the key it
names is absent from what was authored. A present value of the wrong
type, or an undeclared key, stays where it is judged today. It also
skips issues inside an ADR-0031 region (walked as its own graph) and
inside a ledger `value` slot (`fields.*`, `assignments.*`), where a
malformed envelope is the value-envelope pass's finding. A plain
requirement gets `node-config-key-missing` with a prescription naming
the key and the node type. A requirement a rule of the contract states
gets `node-config-key-required-by-rule`, carrying the contract's own
message: a `notify` with no `template` needs a `title`, and a `lookup`
screen field needs its `reference`.
- **Decision arm.** `decision` is parsed by nothing at run time, so this
arm states what its executor reads: `conditions` present and not `null`
is an array (`decision-conditions-not-array`); every branch is an object
(`decision-branch-not-object`); every branch's `label` is a non-blank
string (`decision-branch-label-missing`, with `found`: `absent`, `null`,
`blank`, or the kind).
- `packages/spec/src/automation/flow-node-expression-paths.ts`: the five
new codes join `FLOW_SLOT_REFUSAL_CODES` and `FlowSlotRefusalParams` in
#20352's shape (a stable kebab-case `code` and typed `params` beside the
message). `FlowNodeConfigRefusal` adds the `path` inside `config`. One
walk fix: an ARRAY element is no longer read as an object missing its
slot, so `conditions: [['true']]` gets one refusal (a branch that is not
an object), not also a missing `expression`.
- `packages/spec/src/automation/flow.zod.ts`: a `FlowSchema.superRefine`
block calls the judge for every node, walked with `collectFlowGraphs`,
so a node in a region body is anchored where the author wrote it. It is
a presence rule, not a key-set closure.
- `registerFlow`: no engine change. It parses first
(`canonicalizeStoredFlow` → `FlowSchema.parse`), after the ADR-0087
conversions, so the parse's issue is what it throws.
- `packages/lint/src/validate-expressions.ts`:
`validateStackExpressions` calls the same judge, for a stack handed to
it with no parse in front. One exception: a `script`'s absent `function`
stays the existing callable check's finding. That check reads the
pre-conversion spellings this pass may be handed (the `functionName`
alias, the retired dispatch keys) and names each.
**Route choices.**
1. **The judge parses the executor's contract and keeps absences. It is
not a hand-written table of required keys.** Business need: the census
is 18 keys over 13 node types, and the contract already states every one
of them, rule-required keys included. Long-term design: a table beside
the contracts would be a second statement of them, free to drift; the
ratchet below reads the executors' own parse calls instead. Guarding AI
authors: both forms refuse loudly, but this one uses the contract's own
words where the contract has them. No scope growth: keeping absence only
leaves every other contract finding exactly where it was.
2. **The label rule is "a non-blank string", wider than "the key is
absent".** Every one of those values sends the matched branch down every
out-edge (measured above), and the repo already has one notion of blank
(`NON_BLANK_STRING`). Refusing only the absent key would leave `label:
''`, which misroutes identically.
3. **A non-array `conditions` is refused as well as a non-object
branch.** It is the container of the same branch walk, and every
non-null non-array value fails the run (measured above).
## Acceptance notes
### Family census: every key a contract-parsing builtin's executor
requires
Enumerated from the code: every `parseNodeConfig(…)` call under
`service-automation/src/builtin/`, cross-checked against the
expression-path ledger's required-key reconciliation. The channels
require `loop.collection` and `map.collection`, which that ratchet had
recorded as "no door refuses their absence". It now asserts the judge
refuses both. Door status was measured by the probes above on
`a88a1bb399` (before) and on this branch (after), each row beside its
whole-config accept control, which is admitted at every door both before
and after.
| node kind | key left out | runtime reader | before: parse / register /
validate | after: all three doors |
|:--|:--|:--|:--|:--|
| `get_record` | `objectName` | `crud-nodes.ts:264` | admit / admit /
admit | refused at `config.objectName` |
| `create_record` | `objectName` | `crud-nodes.ts:335` | admit / admit /
admit | refused |
| `update_record` | `objectName` | `crud-nodes.ts:484` | admit / admit /
admit | refused |
| `delete_record` | `objectName` | `crud-nodes.ts:578` | admit / admit /
admit | refused |
| `notify` | `recipients` | `notify-node.ts:250` | admit / admit / admit
| refused |
| `notify` | `title` (no `template`; rule) | `notify-node.ts:250` |
admit / admit / admit | refused, in the contract's words |
| `http` | `url` | `http-nodes.ts:112` (parsed after interpolation;
interpolation never adds a key) | admit / admit / admit | refused |
| `screen` | `fields[i].name` | `screen-nodes.ts:158` | admit / admit /
admit | refused at `config.fields.i.name` |
| `screen` | `fields[i].options[j].value` / `.label` |
`screen-nodes.ts:158` | admit / admit / admit | refused |
| `screen` | `fields[i].reference` on a `lookup` field (rule) |
`screen-nodes.ts:158` | NOT MEASURED before (same arm) | refused, in the
contract's words (spec pin) |
| `script` | `function` | `screen-nodes.ts:321` | admit / admit / REFUSE
(the lint callable check) | refused at all three |
| `subflow` | `flowName` | `subflow-node.ts:71` | admit / admit / admit
| refused |
| `map` | `collection` | `map-node.ts:96` | admit / admit / admit |
refused |
| `map` | `flowName` | `map-node.ts:96` | admit / admit / admit |
refused |
| `loop` (with `body`) | `collection` | `loop-node.ts:85` (the no-`body`
form returns at `:72` unparsed) | admit / admit / admit | refused |
| `parallel` | `branches` | `parallel-node.ts:68` | admit / admit /
admit | refused |
| `try_catch` | `try` | `try-catch-node.ts:100` | admit / admit / admit
| refused |
| `decision` | a branch `label` (absent, `null`, blank, non-text) |
`logic-nodes.ts:48-74` (`branchLabel: cond.label`) | admit / admit /
admit | refused at `config.conditions.i.label` |
| `decision` | a non-object branch; a non-array `conditions` |
`logic-nodes.ts:48-51` | admit / admit / admit | refused |
Controls that stay admitted: a legacy `loop` with no `body` and no
`collection` (it still runs); a decision with no `conditions`,
`conditions: null` or `[]`; `assignment`, `wait` and plugin node types;
a present value of the wrong type (`objectName: 42`); an undeclared key.
Outside this census's definition (no `parseNodeConfig`), measured, **not
fixed here**: `connector_action` with no `connectorConfig` is admitted
at all three doors and refused at run (`connectorConfig.connectorId and
.actionId are required`). It belongs to the same family, but its input
is a FlowNode sibling block, like `wait`'s `waitEventConfig`, which
`FlowSchema` already requires. Reported to the seat.
### Producer census
- `examples/**` at this head: app-crm 1 flow, 8 nodes; app-showcase 30
flows, 139 nodes; app-todo 4 flows, 26 nodes. **0 refusals.**
app-multi-package declares no flows.
- `packages/**`, non-test: no default flow carries a judged node. The
Studio create seed for `flow` has `nodes: []`.
- objectui at the `.objectui-sha` pin `f8a9d0fb` (not edited):
- `FlowObjectListField` `rowsToList` drops a blank cell. So a decision
branch row with an empty **Label** cell is written as `{ expression }`,
which is now refused (`decision-branch-label-missing`). The same writer
serves the screen **Fields** repeater, so a field row with an empty
**Name** cell is written without `name`, also refused.
- `previews/flow-canvas-parts.tsx` `defaultNodeExtras` seeds a new node
with no config, or a partial one: `http` gets `{ method: 'GET' }` (no
`url`), `notify` gets `{ channels, recipients: [] }` (no `title`), and
CRUD / `script` / `subflow` / `map` / `parallel` / `try_catch` get
nothing. So a node added and saved before it is configured is now a loud
save error. The designer's live Zod pass (`clientValidation.ts` →
`FlowSchema`) will locate it at the node's config path once objectui
takes this spec.
- cloud: NOT MEASURED (no checkout in this container; the dispatch named
examples, packages and objectui).
### Fixture triage (disposition per fixture, never a batch rename)
- **Completed** (the fixture was never runnable; it now carries the key
its executor requires): skeleton flows in `spec` `flow.test.ts`,
`region-normalization.test.ts`, `flow-region-pause-and-end.test.ts`,
`api/zod-issues-to-fields.test.ts`; `service-automation`
`engine.test.ts` and 7 sibling files where `script` / `notify` / `map`
nodes stand in for mock executors; `runtime`
`automation-put-post-error-parity`, `automation-register-error-class`
and `automation-flow-clone`; `types` `validation-failure.test.ts`;
`verify` `automation-trigger-terminal-messages.test.ts`. The two
`collectFlowGraphs` scope and path pins now chain through `try` as well
as `catch`, because a `try_catch` without `try` is refused.
- **Re-judged in place** (the pin was about the executor's refusal of a
missing key, which the build door now answers first):
`config-parse.test.ts` (7), `guard-refusal-inventory.test.ts` (7 rows),
`http-nodes`, `notify-node` (2), `subflow-node`. Each now asserts the
door refusal, then registers the node WHOLE and strips the key from the
stored flow, so the executor's guard is still pinned.
`screen-nodes.test.ts`: a stored `script` carrying only retired dispatch
keys no longer registers (`leaves out function`), rather than
registering and refusing at run. `conversions.test.ts`: the flow parse
is still blind to the tombstones, and now refuses the stripped node for
its absent `function`.
- The lint's `script` callable tests are unchanged. That check keeps a
`script`'s `function`, as described above.
### Observed, not filed
- A pre-conversion alias spelling (`object`, `flow`, `functionName`,
`to` / `subject`) is refused as the canonical key's absence at a DIRECT
`FlowSchema.parse` / `defineFlow()`. `registerFlow`, `objectstack
validate` and `defineStack` convert first, so no measured producer meets
it. Carrier: whoever retires those D2 aliases at 18 (the object alias's
own docblock says it retires then); otherwise none.
- A present value of the wrong type (`objectName: 42`, `collection: ''`,
`function: ''` outside the lint) is still admitted at the build doors
and refused at run. That is the same family's type half, which this
card's direction scoped out. Reported to the seat.
## ADR-0087
- One semantic D3 entry for the family,
`flow-node-config-required-keys-refused` (major 18). It uses form D: the
decision is stated in words, with no tracker numbers in author-shown
text. `registry.ts` is regenerated.
- **No D2 conversion.** No lossless repair exists: the platform cannot
know the object, URL, collection, function or out-edge label that was
left out, and no value it could write would keep what the flow did.
- Changeset
`.changeset/20316-flow-node-config-required-keys-refused.md`:
`@objectstack/spec` and `@objectstack/lint` `minor`, BREAKING, with the
FROM → TO table. `service-automation` gets none, because its diff is
test files only.
## Pins: each refusal at each door, with the lit control
- `spec` `flow-node-config-required.test.ts` (`FlowSchema.parse`):
- every census row, refused at its key with the judge's own message,
beside its whole-config accept control;
- the two rule-required keys, in the contract's own words;
- region nesting;
- the decision arm: six label values, four non-object branches, three
non-array `conditions`;
- controls: legacy loop, wrong type, undeclared key, value slot,
`assignment` and plugin types, no-branch decisions.
- `spec` `flow-slot-refusal-codes.test.ts`: one pin per new code (code,
params, full message), the closed set split over three producers, a
sweep that reaches every node-config code, and type-level pins.
- `service-automation` `node-config-required-keys.test.ts`
(`registerFlow`):
- the same census, and the decision arm;
- on the real executors: the refused shapes failed the run, and a
matched branch with no label ran GREEN down both out-edges while its
labelled twin took one.
- `service-automation` `builtin/node-config-contract-ledger.test.ts`:
the ratchet. The map equals every executor's `parseNodeConfig` call,
schema by identity; `loop` alone parses on a condition; every builtin
type is classified.
- `service-automation` `config-expression-ledger.test.ts`: the
cross-check described above.
- `lint` `validate-expressions.test.ts` (`validateStackExpressions`):
the same judge through the third door's pass.
## Ablation: the pins can fail
The ablation ran once, on the committed state `7f3b9b6742`, in one
`os-verify-lock` hold. It went through `scripts/ablation-replace.mjs` in
wrap mode, with the restore trapped on EXIT, INT and TERM.
- **Mutation.** `flowNodeConfigRefusals` returns `[]` unless a global
named `ABLATION_20316_OFF` is set. The anchor went from 1 hit to 0, and
the blob from `bd1d620a25b6` to `353f92191f9d`. After `pnpm --filter
@objectstack/spec build`, `ablation-dist-preflight.mjs @objectstack/spec
ABLATION_20316_OFF` found the marker in 20 built files.
- **Red, in the expected direction:**
- spec, the two pin files: 41 failed, 35 passed. Every refused row at
`FlowSchema.parse` went red, as did the five new-code pins and the
sweep. Every accept control and CONTROL block stayed green.
- service-automation, three files: 21 failed, 47 passed. Every refused
census and decision row at `registerFlow` went red, as did the ledger
cross-check. The accept controls, the run-time "what it did" pins and
the contract-ledger ratchet stayed green.
- lint `validate-expressions.test.ts`: 7 failed, 345 passed. The new
rows went red; the callable-check rows stayed green.
- **Restore leg.** The blob is back to `bd1d620a25b6`, equal to HEAD,
and `git diff HEAD` is empty. After a rebuild, `--absent` found the
marker in none of the 222 built files, and the working tree is clean.
Then spec 76/76, service-automation 68/68, lint 352/352.
## Verification
Branch head `8171d8537e`: `origin/main` `15bf186f50` merged as
`725ffa325e`, plus test-only commits after it. Every heavy run went
through `os-verify-lock`, on a closure built from the merge head (`turbo
run build --filter=@objectstack/cli...`, 59/59). The box was shared.
- **spec**, full suite, both projects: 593 files, 17196 passed, 1 todo.
Run on `725ffa325e`; no spec source changed after it.
- **service-automation**, full: 149 files, 1818 passed. Run on
`725ffa325e`; no source changed after it.
- **lint**, full: 113 files, 4712 passed, on `7f3b9b6742`.
`validate-expressions.test.ts` again on `8171d8537e`: 352/352.
- **runtime** `src/domains`: 77 files, 1436 passed, on `7f3b9b6742`.
- **Smaller suites.** types: 23 files, 692 passed. verify: 15 files, 116
passed. metadata-protocol, its 10 flow files: 207 passed.
plugin-approvals, its 5 flow files: 344 passed. cli `--project unit`,
its flow-building files: 8 files, 112 passed.
- **Typecheck** exits 0 for spec (test layer included),
service-automation, lint, runtime, types and verify.
- **Gates.** `dispatch-gates.mjs --commands` on `8171d8537e` derives 102
families; 100 exit 0. `--ran` accounts for all 102: 100 run, 2 NOT
MEASURED.
- `check:dual-build-cjs-loads`: PREREQUISITE NOT MET. 9 packages outside
this diff have no `dist/`.
- `check:type-check-debt`: its `--re-measure` runs a whole-tree build
outside the lock. This diff adds no package.
- **eslint.** `eslint --no-inline-config --format json` over this
branch's 36 changed `.ts` files: 36 files, 0 errors, 0 warnings.
- Population: `eslint.config.mjs` has `files:
['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}']`, and none of the 36 files is
ignored.
- Invariance: the config sets no `parserOptions.project`, so no linting
is type-aware, and this diff cannot move a verdict on an untouched file.
- **Declared narrowing.** Consumer packages beyond these ran on their
flow-bearing test files only. The rest of the consumer farm is CI's: cli
integration tests, dogfood, and the whole suites of runtime,
metadata-protocol and plugin-approvals.
- **Behind `main`.** The branch is 7 commits behind `origin/main`
`7db1332f19`. A merge-tree probe with no merge driver (a bare shared
clone) merges clean. `check:migration-registry` on that probe merge
reports `registry.ts` current.
## Governed surface
`skills/objectstack-automation/references/_index.md` is regenerated by
`gen:skill-refs` (byte-equal to the generator). `flow.zod.ts` now
reaches `schemaless-node-config.zod.ts` transitively, so the index gains
that one dependency row. Readings: that file 42 → 43 lines; the
published catalog's `SKILL.md` total 4402 → 4402, unchanged.
## 维护者速读(草稿)
-
**改了什么**:流程节点的配置如果缺了执行器必需的字段(比如查询节点没写对象名、循环节点没写要遍历的集合、决策分支没写标签),现在保存或校验时就会直接报错,不再等到流程运行时才失败或走错分支。
-
**为什么改**:以前这类流程能正常保存、正常发布,运行到那个节点才失败;决策分支没写标签更糟,流程会"成功"但同时走了所有分支,业务上悄悄出错。
- **风险与代价(含回滚)**:已有流程里若有这类残缺节点,升级后会被拒绝注册(启动日志会点名),需要补上字段;Studio
设计器里新加的节点在配置完成前保存会报错。回滚即撤销本 PR。
- **席位意见**:
- **你要做的**:无需操作;如需确认,关注 objectui 设计器对"空标签行、未配置新节点"的保存报错体验(另立卡跟进)。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QcAS3qiYYZNezaxZxaUdMV)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent f2c7eef commit 7dc45eb
40 files changed
Lines changed: 1831 additions & 143 deletions
File tree
- .changeset
- packages
- lint/src
- runtime/src/domains
- services/service-automation/src
- builtin
- spec
- api-surface
- export-origins
- src
- api
- automation
- conversions
- migrations
- entries/semantic
- types/src
- verify/src
- skills/objectstack-automation/references
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
| |||
2291 | 2292 | | |
2292 | 2293 | | |
2293 | 2294 | | |
2294 | | - | |
| 2295 | + | |
2295 | 2296 | | |
2296 | 2297 | | |
2297 | 2298 | | |
| |||
2341 | 2342 | | |
2342 | 2343 | | |
2343 | 2344 | | |
2344 | | - | |
| 2345 | + | |
2345 | 2346 | | |
2346 | 2347 | | |
2347 | 2348 | | |
| |||
3010 | 3011 | | |
3011 | 3012 | | |
3012 | 3013 | | |
| 3014 | + | |
| 3015 | + | |
| 3016 | + | |
| 3017 | + | |
| 3018 | + | |
3013 | 3019 | | |
3014 | 3020 | | |
3015 | 3021 | | |
| |||
4468 | 4474 | | |
4469 | 4475 | | |
4470 | 4476 | | |
| 4477 | + | |
| 4478 | + | |
| 4479 | + | |
| 4480 | + | |
| 4481 | + | |
| 4482 | + | |
| 4483 | + | |
| 4484 | + | |
| 4485 | + | |
| 4486 | + | |
| 4487 | + | |
| 4488 | + | |
| 4489 | + | |
| 4490 | + | |
| 4491 | + | |
| 4492 | + | |
| 4493 | + | |
| 4494 | + | |
| 4495 | + | |
| 4496 | + | |
| 4497 | + | |
| 4498 | + | |
| 4499 | + | |
| 4500 | + | |
| 4501 | + | |
| 4502 | + | |
| 4503 | + | |
| 4504 | + | |
| 4505 | + | |
| 4506 | + | |
| 4507 | + | |
| 4508 | + | |
| 4509 | + | |
| 4510 | + | |
| 4511 | + | |
| 4512 | + | |
| 4513 | + | |
| 4514 | + | |
| 4515 | + | |
| 4516 | + | |
| 4517 | + | |
| 4518 | + | |
| 4519 | + | |
| 4520 | + | |
| 4521 | + | |
| 4522 | + | |
| 4523 | + | |
| 4524 | + | |
| 4525 | + | |
| 4526 | + | |
| 4527 | + | |
| 4528 | + | |
| 4529 | + | |
| 4530 | + | |
| 4531 | + | |
| 4532 | + | |
| 4533 | + | |
| 4534 | + | |
| 4535 | + | |
| 4536 | + | |
4471 | 4537 | | |
4472 | 4538 | | |
4473 | 4539 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
| 90 | + | |
90 | 91 | | |
91 | 92 | | |
92 | 93 | | |
| |||
1620 | 1621 | | |
1621 | 1622 | | |
1622 | 1623 | | |
| 1624 | + | |
| 1625 | + | |
| 1626 | + | |
| 1627 | + | |
| 1628 | + | |
| 1629 | + | |
| 1630 | + | |
| 1631 | + | |
| 1632 | + | |
| 1633 | + | |
| 1634 | + | |
| 1635 | + | |
| 1636 | + | |
| 1637 | + | |
| 1638 | + | |
| 1639 | + | |
| 1640 | + | |
| 1641 | + | |
| 1642 | + | |
| 1643 | + | |
| 1644 | + | |
| 1645 | + | |
1623 | 1646 | | |
1624 | 1647 | | |
1625 | 1648 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
103 | | - | |
| 103 | + | |
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
49 | 51 | | |
50 | 52 | | |
51 | 53 | | |
| |||
127 | 129 | | |
128 | 130 | | |
129 | 131 | | |
130 | | - | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
131 | 137 | | |
132 | 138 | | |
133 | 139 | | |
134 | 140 | | |
135 | | - | |
| 141 | + | |
136 | 142 | | |
137 | 143 | | |
138 | 144 | | |
| |||
141 | 147 | | |
142 | 148 | | |
143 | 149 | | |
144 | | - | |
| 150 | + | |
145 | 151 | | |
146 | 152 | | |
147 | 153 | | |
148 | 154 | | |
149 | | - | |
| 155 | + | |
150 | 156 | | |
151 | 157 | | |
152 | 158 | | |
| |||
161 | 167 | | |
162 | 168 | | |
163 | 169 | | |
164 | | - | |
| 170 | + | |
165 | 171 | | |
166 | 172 | | |
167 | 173 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
74 | 74 | | |
75 | 75 | | |
76 | 76 | | |
77 | | - | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
78 | 80 | | |
79 | 81 | | |
80 | 82 | | |
| |||
159 | 161 | | |
160 | 162 | | |
161 | 163 | | |
162 | | - | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
163 | 169 | | |
164 | 170 | | |
165 | 171 | | |
166 | 172 | | |
167 | | - | |
| 173 | + | |
168 | 174 | | |
169 | 175 | | |
170 | 176 | | |
| |||
173 | 179 | | |
174 | 180 | | |
175 | 181 | | |
176 | | - | |
| 182 | + | |
177 | 183 | | |
178 | 184 | | |
179 | 185 | | |
180 | 186 | | |
181 | | - | |
| 187 | + | |
182 | 188 | | |
183 | 189 | | |
184 | 190 | | |
| |||
193 | 199 | | |
194 | 200 | | |
195 | 201 | | |
196 | | - | |
| 202 | + | |
197 | 203 | | |
198 | 204 | | |
199 | 205 | | |
| |||
0 commit comments