Repository navigation
Commit 54fb60a
fix(service-automation)!: a flow the kernel:ready cold-boot bind refuses is withdrawn, not left registered and active from the boot pull (#21897)
Fixes #21848
Clause-②: no (narrowing)
## Current scope: patch round 1 (after #21893 landed), what was dropped
and what remains
**Dropped.** #21893 (merged as `866683f96f`) made the approval node's
config contract part of `FlowSchema`: the flow parse now judges an
approval node's config against `ApprovalNodeConfigSchema`, whole.
`registerFlow` parses `FlowSchema` first, so the admin door and the
package-load boot pull already refuse an out-of-range escalation and an
undeclared escalation key, located at the config path. Per the seat's
decision `5998929933`, there is now one judge in the spec, so this PR
drops its own:
- `NodeExecutor.configContract` (the optional published member) is
removed, and so are `AutomationEngine.validateNodeConfigValues` and its
call in `registerFlow`. `engine.ts` is byte-identical to `main` (blob
`597e506a7711`).
- The `formatIssuePath` export in `builtin/parse-config.ts` is removed
(nothing else needed it). The file is byte-identical to `main`.
- `plugin-approvals` ends with no change. `approval-node.ts` is
byte-identical to `main` (blob `e2027bb0c675`).
`approval-node-config-contract.test.ts` is deleted: everything it pinned
depended on `configContract`, and the engine-level refusal it also
covered is #21893's pin.
- The changeset no longer names `@objectstack/plugin-approvals`.
**Remains.**
- **The cold-boot withdraw** (`service-automation/src/plugin.ts`). This
is a separate, measured defect. The boot pull registers a package's
flows before a plugin that contributes a node type has registered its
executor, and with it the descriptor `configSchema` the key check reads,
so the pull registers such a flow unchecked and arms it. The
`kernel:ready` bind then re-registers the flow and refuses it, but used
to only warn, so the flow stayed `active` and bound. The bind now
withdraws a flow it refuses, and only that flow. Since #21893, approval
configs are refused at the boot pull itself, so the withdraw is measured
on a plugin node type that the spec does not declare.
- **The door pins**
(`packages/qa/dogfood/test/flow-node-config-values-at-registration.dogfood.test.ts`)
now pass through the spec's judge. Re-measured at `7333fd869d`: 6
passed.
- **Package load.** The sub-hour flow and the undeclared-key flow both
answer `404`. The boot line names each flow and the located path
(`nodes[1].config.escalation.timeoutHours`,
`nodes[1].config.escalation.bogusKey`). The valid escalation registers
`active` and runs: it opens one pending approval request.
- **Package load, plugin node type.** A flow with an undeclared key on
that node type is refused by the `kernel:ready` bind and answers `404`.
Its valid sibling answers `200`.
- **Admin door.** Both approval refusals answer `400 VALIDATION_FAILED`,
with `details.fields` carrying `nodes.1.config.escalation.timeoutHours`
and `nodes.1.config.escalation.bogusKey` respectively. `GET` answers
`404` for both. The valid escalation registers.
- Only the assertions that named this PR's old message text changed:
`node 'gate'` became the spec's located path.
- **Unit pin**
`service-automation/src/flow-cold-boot-refusal-withdraw.test.ts`
replaces `node-config-values-at-registration.test.ts`. The withdraw is
tested on a real `LiteKernel`, with the plugin node type registered from
a later plugin's `start()`. The control shows that the same body
registers when no plugin contributes the type.
- **Ablation** (`scripts/ablation-replace.mjs`): the withdraw call was
replaced by a marker statement (blob `e36e7077781b` to `5f67fd9d27bd`)
and present in `dist/` per `ablation-dist-preflight`. Results: unit 1
failed / 1 passed (the withdraw pin red, the control green); dogfood 1
failed / 5 passed (only the plugin-node package-load pin, `200 active`).
Restore: blob equals HEAD and `git diff HEAD` is empty. After a rebuild,
`--absent` reads the marker absent from all 6 built files, with the tree
clean.
- **Clause-②** returns to `no (narrowing)`. Nothing published widens
now, and the withdraw narrows what stays loaded at boot. The changeset
is `minor` with the `!` banner for `@objectstack/service-automation`
only. The ADR-0087 marker stays `not-required
(no-migration-prescription)`.
- **Docs.** `content/docs/automation/flows.mdx` now says the flow parse
judges an approval node's config whole, and that a flow refused at boot
is not left registered.
_Seat's restructure (`domain:services` seat 1, #6021): the section below
is the dev's patch-round-1 notes and states what this PR ships now.
Everything under **Round 0 record** describes the first build; where it
names `NodeExecutor.configContract`, `validateNodeConfigValues`, the
`formatIssuePath` export or the approval executor's declaration, it is
superseded (seat decision `5998929933` on #21848)._
---
## Round 0 record
## What this changes
A flow node's config VALUES are now judged where the flow registers, by
the schema the node's executor parses at run time. Before, registration
read a node's config against the descriptor's JSON-Schema `configSchema`
for its key NAMES only, so an approval node with
`escalation.timeoutHours: 0.5` (the contract says `>= 1`) registered
through `POST /automation`, loaded `active` from a package, and then
failed every trigger-fired run at the approval node with no approval
request opened.
- **`service-automation/src/engine.ts`.** `NodeExecutor` gains an
optional `configContract` (the structural `safeParse` view the builtin
executors already parse through, `NodeConfigContract`). `registerFlow`
runs a new `validateNodeConfigValues` right after the key-name check:
every node whose executor declares a contract has its `config ?? {}`
parsed with it, in every graph (`collectFlowGraphs`, so region bodies
too), and any finding refuses the flow. The refusal mirrors the key-name
one: a plain `Error` (no `code` or `status` of its own), the flow name,
then one line per finding with the node id, its type and the config path
in the execute-time spelling (`config.escalation.timeoutHours`),
followed by the contract's own sentence. No hand-written value check and
no JSON-Schema validation: the executor's own contract is the one judge.
- **`plugin-approvals/src/approval-node.ts`.** The approval executor
declares `configContract: ApprovalNodeConfigSchema`, the very object its
`execute` already parses. `ApprovalAutomationSurface` gains the matching
optional member. `execute` is unchanged.
- **`service-automation/src/plugin.ts` (measured necessity, H5).** The
`kernel:ready` cold-boot bind withdraws a flow it refuses. See H5 below
for why the package-load door needed this.
- **`service-automation/src/builtin/parse-config.ts`.**
`formatIssuePath` is exported from the module so both refusals spell
paths alike. The package entry (`index.ts`, the only `exports` entry)
does not re-export it, so no symbol is added to any package entry.
- **`content/docs/automation/flows.mdx`.** The `config` row and the
config callout now say registration also judges values for a node type
whose executor declares its contract.
- **Changeset**
`.changeset/21848-node-config-values-at-registration.md`: `minor` for
both packages, `!` banner, `Clause-②: no (narrowing)`, handling =
correct the value at the located path.
## Mechanism hypotheses, measured
- **H1, confirmed.** A red pin on `origin/main` (the dogfood file below
at `be7450c78e`, before the fix): `GET
/automation/esc_value_packaged_sub_hour` answered `200` with `status:
active`; `POST /automation` with the same body answered `200`; the
trigger-fired run failed with `Approval node 'gate' has invalid config:
escalation.timeoutHours: Too small: expected number to be >=1`. 3
failed, 2 passed (the two controls).
- **H2.** Registration learned a plugin node's config keys only from
`descriptor.configSchema`, a JSON Schema (the approval node publishes
`getApprovalNodeConfigJsonSchema()`, a `z.toJSONSchema` projection). No
node type registered the Zod schema `execute` parses. The smallest
change that reaches it is an optional executor member, so it touches
`service-automation` (the member and the judge) and `plugin-approvals`
(the declaration). JSON-Schema validation was not used: the projection
drops what JSON Schema cannot say, and the approval contract has such a
rule (a `superRefine` refusing `fallbackApprovers` beside a policy that
never reads it), which this PR refuses at registration and pins.
- **H3.** Node types that declare no contract keep today's behaviour
(key names only, where a descriptor publishes `configSchema`). After
this PR that is every node type except `approval`: all builtins
(`get_record`, `create_record`, `update_record`, `delete_record`,
`notify`, `http`, `screen`, `script`, `subflow`, `map`, `loop`,
`parallel`, `try_catch` parse their contract inside `execute`;
`decision`, `assignment`, `wait`, `connector_action` are schemaless or
read sibling blocks), `approval_revise` (schemaless, parses nothing;
pinned that it declares no contract), and every third-party node type.
No schema was invented for any of them.
- **H4, confirmed.** Located refusal, same class as the key-name
refusal. Through the admin door the body is `400` with `error.code:
VALIDATION_FAILED`, message `Flow 'esc_probe_door' rejected: 1 config
value(s) the node's own contract refuses.` then ` - node 'gate'
(approval): config.escalation.timeoutHours: Too small: expected number
to be >=1`, and `details.fields[0]` = `{ field: '(body)', code:
'invalid_value' }`, exactly the envelope the undeclared-key refusal gets
(the runtime domain's `flowDefinitionRefusal` maps any plain error from
`registerFlow` this way).
- **H5, measured, and it needed a second change.** On `main` the boot
pull (`AutomationServicePlugin.start()`) registers package flows BEFORE
`ApprovalsServicePlugin.start()` registers the `approval` executor (boot
log: the three `Flow registered: esc_value_packaged_*` lines, then `Node
executor registered: approval` about 12 ms later), so the pull cannot
judge approval nodes. The `kernel:ready` bind re-registers every flow
once the executor exists, and refused the flow with a WARN, but the boot
pull's registration stayed: measured with an `escalation.bogusKey` flow
(the existing key-name refusal) that the WARN refused while `GET
/automation/...` kept answering `200 active` and the record-change
trigger stayed bound. The same hole would have swallowed this PR's value
refusal at package load. The bind now withdraws a flow it refuses
(`withdrawFlow`, only the refused name; a failed or empty READ still
tears nothing down). After: the sub-hour flow and the bogus-key flow
both answer `404` after boot, each with one located WARN (`[Automation]
cold-boot flow bind: failed to register flow`), and the rest of the
package loads: only the flow is refused, never the package, matching how
the boot pull already treats a flow it refuses.
## Tests
All at `5fdd32adc9` (the final commit) unless named otherwise:
- `pnpm --filter @objectstack/service-automation exec vitest run
--maxWorkers=2 src/node-config-values-at-registration.test.ts`: 7
passed. Engine pins: 0.5 refused with flow, node and path, and nothing
registered; the value refusal and the key-name refusal are the same
class (plain `Error`, no `code`, no `status`, same prototype); a valid
escalation registers unchanged; the `superRefine` rule refuses at
`config.onEmptyApprovers`; a region-body node is judged and named with
its region; an executor without a contract keeps key-name-only
judgement. Boot pin (real `LiteKernel`, boot pull plus protocol view
serving the same packaged flows, the executor registered from a later
plugin's `start()`): the refused flow is withdrawn and unbound, the
valid one stays bound.
- `pnpm --filter @objectstack/plugin-approvals exec vitest run
--maxWorkers=2 src/approval-node-config-contract.test.ts`: 2 passed. The
declared contract is `ApprovalNodeConfigSchema` itself (identity),
`execute` refuses what it refuses, `approval_revise` declares none; on
the real engine 0.5 is refused, located, and `timeoutHours: 1`
registers.
- `packages/qa/dogfood`, new file
`test/flow-node-config-values-at-registration.dogfood.test.ts` (`vitest
run --project isolated --maxWorkers=2`): 5 passed. Package load: the
sub-hour flow answers `404`, and a boot line names the flow, `node
'gate'` and `escalation.timeoutHours`; the valid escalation in the same
package registers `active` and runs (a record of its object opens one
pending approval request, `pending_approvers` = the position). Admin
door: `400`, `VALIDATION_FAILED`, the message names the flow, `node
'gate'` and `escalation.timeoutHours`, and `GET` answers `404`; a valid
escalation registers.
- Full suites at `2ef0c612b2` (the code is byte-identical at
`5fdd32adc9`: `git diff 2ef0c61..HEAD -- packages/services
packages/plugins packages/qa` is empty):
`@objectstack/service-automation` 173 files, 2117 tests passed;
`@objectstack/plugin-approvals` 61 files, 897 tests passed.
- `pnpm --filter @objectstack/service-automation --filter
@objectstack/plugin-approvals run typecheck`: both `Done` (`tsc
--noEmit` plus `check:test-typecheck`, which compiles the test layer).
`pnpm --filter @objectstack/dogfood run typecheck`: exit 0, and `tsc
--listFilesOnly` shows the new dogfood file in the program.
- Census of every approval node in the example stacks and dogfood
fixtures (showcase, crm, multi-package, the four approval fixtures),
parsed with `ApprovalNodeConfigSchema`: 36 flows, 19 approval nodes, 0
refused, so no shipped flow stops registering.
## Ablations (the refusal pins can fail)
Both through `scripts/ablation-replace.mjs` (WRAP mode, its own restore
trap) on the committed fix, `service-automation` rebuilt inside the leg
and `scripts/ablation-dist-preflight.mjs` proving the marker in `dist/`
before any suite ran.
- **A: the key-name-only check put back** (the
`validateNodeConfigValues` call replaced by a marker statement; anchor 1
to 0, blob `b5e94fb23b67` to `bd7099e9875b`). Preflight: marker present
in 2 built files. The build's DTS step exited 1 (TS6133: the ablation
leaves the private judge unused); the ESM and CJS bundles the suites
load were emitted, which the preflight reading confirms. Results:
service-automation 5 failed / 2 passed (the 2 are the valid-escalation
and no-contract controls); plugin-approvals 1 failed / 1 passed; dogfood
3 failed / 2 passed (package-load 404, the boot line, the admin-door
400; the two controls pass).
- **B: the cold-boot withdraw removed** (anchor 1 to 0, blob
`fb06fd8ad148` to `0d523279cf1c`). Preflight: marker present in `dist/`.
Results: service-automation 1 failed / 6 passed (only the boot pin);
plugin-approvals 2 passed; dogfood 1 failed / 4 passed (only
package-load `404`; the boot line still locates the value, so the two
halves are independent).
- **Restore, both legs:** blob after restore equals the HEAD blob and
`git diff HEAD` is empty (the tool's own reading); then
`service-automation` rebuilt (exit 0) and `ablation-dist-preflight
--absent` for both markers: absent from all 6 built files, working tree
clean.
## Gates
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at `5fdd32adc9` derived 95 commands; every
one was run with its exit code recorded, and `--ran` answered `95
derived, 95 run, 0 NOT-MEASURED, 0 UNRUN` (exit 0). Three needed a
rerun, each for a measured cause outside this diff:
`check-comment-mask-corpus` read a scratch probe file mid-deletion
(rerun 0); `spec check:skill-examples` refused on a `packages/spec` dist
built before the `main` merge (spec rebuilt, all cache hits; rerun 0);
`check:dual-build-cjs-loads` refused on 8 packages with no `dist/`
(built, all cache hits; rerun 0). The 17 commands the dispatch named
beyond that derivation (the spec export, ledger and doc families) were
also run: all exit 0. ESLint on the 7 touched TypeScript files
(`--no-inline-config --format json`): 7 files, 0 errors, 0 warnings; the
config's own globs cover all 7, and `eslint.config.mjs` enables no
type-aware linting, so this diff cannot move any untouched file's
verdict. The repo-wide lint is CI's.
## ADR-0087 disposition
`not-required (no-migration-prescription)`, in the changeset marker,
with no D3 ledger entry and no `packages/spec` edit: no authorable key,
spelling, export or stored shape moves, `ApprovalNodeConfigSchema` and
`FlowSchema` parse exactly what they parsed, `execute` accepts what it
accepted, and which value an author meant is not something a ledger
entry can rewrite. This is the disposition this repo's other runtime
refusals of what already failed later declared.
`check-adr-0087-registration --base origin/main`: exit 0.
## Overlap with the build-door card
#21850 (claimed in `domain:spec`, in flight on
`claude/issue-21850-approval-node-config-build-refusal`) is not
addressed here: this PR does not touch `objectstack validate` /
`compile`, `flow-node-config-refusals.ts` or any spec file. Its WIP
judges the approval contract whole inside `FlowSchema`'s superRefine,
which `registerFlow` parses first; once it lands, an approval value
refusal will surface from that parse (a Zod error mapped through
`fieldsFromZodIssues`) before this judge is reached, and this judge
keeps covering every node type whose executor declares its contract
without the spec knowing it. The dogfood pins assert the status, the
code and the located path (`escalation.timeoutHours`, `node 'gate'`),
not a full sentence, and build the fixture with `strict: false` so the
build door does not stop the invalid body before the runtime doors it
pins. The measured boot hole above also bears on that card's repro step
3: on `main` the unknown-key flow was not dropped at boot, it stayed
`active` and bound; after this PR it is dropped.
## Acceptance notes
- **The builtin half of the same class** (reported to the seat, not
filed here): a builtin node with a present value its contract refuses
still registers. Measured at `5fdd32adc9`: `POST /automation` with a
`create_record` node carrying `outputVariable: 42` answered `200`, and
`POST /automation/:name/trigger` answered `400 FLOW_FAILED` with `config
does not satisfy the create_record contract — config.outputVariable:
Invalid input: expected string, received number`. Wiring the builtins is
not mechanical (`http` parses after interpolation, `loop` parses
conditionally, the region containers' contracts contain their regions)
and is not in this card's file surface.
- **Executors registered after the cold-boot bind**: a third-party
plugin that registers its executor from its own `kernel:ready` handler,
after this plugin's bind, would leave a boot-pulled flow judged only on
later registrations. No in-repo node type does this (the builtins
register at `init`, `approval` at `start`). Not measured further.
- **The metadata save door** (`PUT /meta/flow/:name`) re-arms through
the mutation sync, whose refusal handling this PR does not change. Not
measured.
---
_Generated by [Claude
Code](https://claude.ai/code/session_011K3zqE8Pv1Evw5hc8tZCnN)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent e6dc7a2 commit 54fb60a
5 files changed
Lines changed: 530 additions & 2 deletions
File tree
- .changeset
- content/docs/automation
- packages
- qa/dogfood/test
- services/service-automation/src
| 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 | |
|---|---|---|---|
| |||
138 | 138 | | |
139 | 139 | | |
140 | 140 | | |
141 | | - | |
| 141 | + | |
142 | 142 | | |
143 | 143 | | |
144 | 144 | | |
| |||
155 | 155 | | |
156 | 156 | | |
157 | 157 | | |
158 | | - | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
159 | 166 | | |
160 | 167 | | |
161 | 168 | | |
| |||
| 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 | + | |
| 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 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
0 commit comments