Commit 3dd4170
authored
Real settle teardown, and peer settle-tuple reconciliation (settle teardown, step 3) (#1076)
* feat: attach real settle teardown, and reconcile peer settle-tuple writes
Step 3 of the settle-teardown design.
The seam is async now. Step 2 shipped a branded synchronous return type that
made an awaited teardown a compile error; real stops are async, so that guard
had to go. It was a tripwire, not an obstacle: it existed because bolting a
deferred teardown onto a synchronous write is what produced a P1 in each of
#1059's six rounds, and what changed is the machinery under it. The settling
window is exclusive, abortable and in-memory, which is exactly what makes it
safe to HOLD across an await — the revision re-check and abort check after the
await are the suspension-point guards, and the race matrix covers both.
Teardown (sessionSettleTeardown.ts) reuses stopLaneRuntimeWork's SHAPE, not its
body: that function disposes chat sessions because it serves lane deletion, and
a settle must leave the session usable. Terminals are never touched. Abort is
checked before each step, so a turn that wins the race keeps its work.
R5 lands as 3d option 3: an unconfirmed stop still settles, with residue
recorded — coarse reason, reapable flag, local-only table, cleared implicitly
when the session is reactivated — plus one bucketed analytics event per settle.
Never silent.
Peer tuple writes (R7 + the unsettle mirror) are fixed by finishing host
authority, not by adding consensus. db.sync.applyChanges is the one place both
the host and peer paths funnel through, so settle-tuple changes are held out of
the raw apply and replayed through the chokepoint, gaining the revision and
window semantics. Held rather than dropped: a paired desktop's decision is
legitimate, it just has to come through the front door. No peer-visible token
was built; onRemoteSettleWrite is the telemetry that decides whether one is
ever justified.
R7/R7b keep their raw db.run bypass and are annotated as to why: they pin the
property that motivates the interception.
* test: cover the residue table in the CRR-exclusion invariant
Step 3 adds a second host-local settle table, so the exclusion test that
guards the revision table now guards both. The positive control on
terminal_sessions is what keeps the assertion falsifiable.
* fix: quality dual-review findings, including two blockers
Track A found two blockers, both real.
1. The brain never wired any of this. apps/ade-cli/src/bootstrap.ts built its
sessionService with no teardown seam and never registered the apply-layer
handler — and in a normal install the brain, not the desktop, is what
applies changesets and serves phone sync, remote commands and the PR-merge
poller. Teardown was a no-op for almost every settle a user can actually
trigger. Both processes now build their hooks from one
createSettleTeardownWiring factory so they cannot drift.
2. Holding settle-tuple rows out of crsql_changes breaks LWW convergence. The
reviewer proved it against the vendored cr-sqlite build: a column that never
enters crsql_changes never raises the local col_version, so this host stays
behind the peer permanently and its NEXT genuine decision loses every merge.
Two hosts then disagree forever — strictly worse than the bypass being
fixed. Reversed: CRR now owns the values and the chokepoint owns the
revision, via an observeRemote intent that self-assigns (matches the row, so
the revision bumps; changes nothing, so no new column version and no echo).
This also dissolves the composed-intent and lost-batch findings, since no
intent is reconstructed and nothing is held.
Also fixed: stop_and_clear was destroying the user's queued turns on every
settle (now stop_only — 3c says losing a settle costs a click, losing the
user's work does not); both new analytics properties were silently dropped by
the sanitizer, so the telemetry the design leans on did not work, now pinned by
a test; unbounded provider awaits could hold the settling window open forever
and leave a row permanently unsettleable; count_bucket measured a value that
was always 1; session_settle_residue leaked past deleteSession; residue
analytics fired for settles that never landed; the settling window could be
closed by an owner that no longer held it.
Track B: settleSession now delegates to the typed form instead of duplicating
it, the provider stop-control fact moved to subagentCapabilities, dead fields
(stopped, scheduled_work, reapable) removed, two orphaned JSDoc blocks
reattached, stale test title and a dangling comment asserting the opposite of
the design deleted, and residue got a read path via the action registry —
'discoverable' was a condition of 3d option 3, not a nice-to-have.
* fix: second-round review findings across teardown, reconcile and latency
Track A verified two load-bearing claims empirically against the vendored
cr-sqlite: the observeRemote self-assignment bumps sqlite3_changes without
touching the clock (no echo), and stop_only really does stop background work.
The design holds. What it found on top:
- session.getSettleResidue was added to CTO_ONLY but not the allowlist, so
every call was refused. The read path 3d option 3 was signed off on did not
actually exist.
- Stale residue survived a clean re-settle: nothing cleared the row when a
later teardown confirmed everything, so it kept reporting an old failure
with an old timestamp.
- A peer write bumped the revision but never tripped the abort. The revision is
only re-read AFTER teardown, so teardown ran to completion and interrupted a
turn the user had just started on the other device — losing the work AND the
settle, which is exactly the R2 shape 3c exists to prevent.
- The reconcile handler fired on changes had discarded, so a
re-delivered batch abandoned an in-flight settle over a duplicate packet.
- A timed-out liveness read was indistinguishable from 'not a chat session', so
a slow host settled while claiming a clean teardown — the one outcome residue
exists to prevent.
- settle_remote_write_reconciled fired on the NORMAL desktop-peer path, one
event per session. 'Expected zero' was wrong: a paired desktop replicating its
own settles belongs here. Now one batched event per changeset, framed as a
rate signal.
- Bulk settle was serial, and per session now costs up to 15s. iOS allows 30s
for the whole command, so three busy sessions was a guaranteed timeout. Now
bounded-concurrent, results reassembled in the caller's order.
- Leaked ~50 unref'd timers per settling session; 10Hz polling of an expensive
read; lmstudio missing from the provider dimension.
Track B: restored the settle methods' locality after my own earlier repair
scattered them, put back two invariant comments that repair dropped, moved the
duplicated analytics envelope into the shared factory, made residue report a
surviving turn separately from surviving jobs, and covered the hand-rolled
cr-sqlite pk decoder — the riskiest code in the diff — with a test that drives
the real applyChanges path.
* fix: third-round review — the dedup guard was inert, and residue could be erased
Track A proved my `result.changes > 0` guard does nothing. `crsql_changes` is
a VIRTUAL table, so SQLite counts the xUpdate call whether or not cr-sqlite
discarded the row as a losing merge — `insert or ignore` never engages, and a
re-applied identical changeset still reports one change. Worse, round 2 had
just made reconciliation trip the abort, so a duplicate packet (the peer's
outbound cursor only advances on an ok ack, so a dropped ack re-sends the same
range) would have killed a user's in-flight settle while carrying no new
information. Replaced with a real value comparison: capture the column before
the apply, report only if it actually moved. The kvDb test now applies the same
changeset twice and asserts nothing is reported the second time — it fails
against the old guard, which is how the inertness reproduces.
Track A also found clearSettleResidue treating 'could not check' as 'confirmed
clean'. An empty residue array is also what you get before the chat service
exists and when the confirmation read times out, so a settle that verified
nothing was deleting an accurate report of work still running. Teardown now
returns an explicit `confirmed` flag and only a confirmed-clean settle may
erase. The no-op fallbacks in both processes return confirmed: false.
Same class, second site: the read-timeout rule was enforced after the first
read but not on the confirmation read, so a hung confirm still returned clean.
readWork is now a discriminated result, which makes the compiler force both
call sites to decide.
Track B: replaced the worker pool with chunking — order is preserved for free,
and perSession, the queue and the reassembly loop all disappear; hoisted the
concurrency constant to module scope; fixed three docblocks my own insertions
had detached from their functions; typed ACTIVITY_ABORTS so a future abort
reason cannot be silently mis-bucketed; restored the items[0] guard; renamed
sessionCount to changesetSessionCount since it counts the changeset, not the
reconciliation; made the concurrency test fail by assertion instead of by
vitest timeout; and pinned action reachability, which is what H1 slipped
through.
Not changed: Track B read the settle methods as still scattered. Verified
against main — the method order is byte-identical to base, so the interleaving
is the pre-existing layout, not damage. Left alone rather than risk a third
structural move in this file.
* docs: correct settle claims that step 3 made false
The terminals README still said settle 'deliberately does NOT stop the
session's background work' and that a peer's CRR write is outside the
revision's scope. Both were true when written and are not now. Replaced with
what actually happens, including why the ordering of steps 0-3 was the thing
that made it work.
The sync docs were accurate about the phone-only column filter but silent on
the desktop-peer path, which is the one a reader would now come looking for.
* docs: record the sync bulk-settle shape as an open wire decision
3c's table says the sync entry point should carry the typed outcome
additively; it still answers with a bare changed-id array, so an aborted id is
indistinguishable from an ineligible one. Not a regression and not silently
wrong — iOS's local overlay expires on its own rather than showing a settled
row — but step 3 makes aborts likelier, and the fix is a wire-compatibility
call that needs the mobile side, so it is written down rather than guessed at.
* fix: fourth-round findings — restore the worker pool, and stop over-claiming confirmation
Track A verified the value-based dedup guard against the real cr-sqlite across
nine scenarios, including the one that actually matters: an exact duplicate
arriving in a LATER applyChanges call is not reported, while a genuinely new
change still is. It also confirmed the snapshot placement, the absence of SQL
injection (the column is narrowed by the type guard before interpolation), and
that a numeric val is safe under TEXT affinity.
Four fixes from that pass:
- Reverted chunking back to the worker pool. I took that simplification last
round and it cost real throughput: a chunk barrier idles the other workers
until its slowest member finishes, and 'every teardown is bounded' is not
'every teardown takes the same time'. Measured at roughly 65s versus 20s for
a 50-session sweep with a quarter of the rows unstoppable — aimed straight at
the 30s iOS budget the concurrency exists to protect. The perSession map
already gave request order, so the simplification bought nothing.
- An abort during the confirmation loop returned confirmed: true. Nothing was
confirmed and the work was still running, which is precisely the shape the
flag was added to make impossible — one refactor away from erasing an
accurate residue record.
- A confirmation-read timeout discarded a provider it had already read, losing
the analytics dimension for the residue most worth attributing.
- The value guard read blobs as null, so a blob that changed looked unchanged.
Out-of-contract for any real writer, but the guard it replaced did report it.
Both new tests were probed and both initially failed to be meaningful: the
provider test was hitting a microtask race where the immediate expire won the
FIRST read, and the confirmation test tripped the abort before the loop it was
meant to exercise, so it passed against the bug. Fixed both, then re-probed —
they now fail against the pre-fix code.
* fix: stop the settle pool when a persistence failure propagates
CodeRabbit, and it is a failure mode this branch introduced: before step 3,
settleMany was one statement for the whole batch, so there was no partial state
to lose. Now each session settles individually, and a SQLite lock thrown from
one of them rejected Promise.all while the other workers kept shifting the
queue — settling sessions the caller had already given up on.
The queue is drained on the first failure so no NEW work starts, the sessions
already in flight finish rather than being abandoned half-written, and the
error is rethrown only once every worker has stopped. Persistence failures
still propagate rather than being dressed up as a settle outcome — that
distinction was an earlier review finding and it stands. What did settle is
durable, and settle is idempotent, so the caller's retry re-reports it instead
of double-filing.
* fix: honor stop_only everywhere, and see background jobs that outlived a restart
Two from Codex, both real, both undermining the point of the feature.
stop_only was only honored on the Claude path. The OpenCode, Cursor, Pi and
Droid branches of interrupt call cancelQueuedSteers unconditionally and return
before the mode is ever consulted — so a settle on those providers silently
deleted the user's queued prompts, which is exactly the unrecoverable loss the
mode was added to prevent. Gated every one of them. The default is
stop_and_clear, so the Stop button is untouched; only teardown asks for
stop_only.
activeBackgroundTaskCount is derived from the LIVE managed runtime, so a Claude
--bg job that survives a brain or app restart reads as zero. Teardown saw a
quiet session, skipped interrupt entirely, and filed the row as settled while
the daemon job kept running — the precise bug this whole feature exists to fix,
reintroduced through a liveness read. The summary already resolved the
persisted job for other consumers; it is now on the type and counted as work.
Both pinned by tests probed against the pre-fix code. Not fixed: CodeRabbit
re-posted the queue-drain comment against the previous head; the drain landed
in d499220. Its second half — surfacing the partial outcome instead of
throwing — is deliberate: a SQLite lock is not a settle outcome, and settle is
idempotent, so the caller's retry re-reports what landed.
* fix: gate the persisted background job on daemon liveness
Codex, and it is a defect my own previous fix created. claudeBackgroundJobShort
is a RECORD, not a liveness signal — it survives the job finishing and survives
teardown stopping it. Counting it unconditionally meant every later settle on
that session would spend the full confirmation budget and then report residue
for a job that no longer exists, while trying to stop it again each time.
Now the daemon is asked, through a narrow hasLiveClaudeBackgroundJob exported
from agentChatService, and only when the live count already says quiet AND a
job is on record — the restart case. That keeps the round-trip off the hot read
while still closing the hole where a job outlives its runtime.
Also documented, not fixed: a provider stop that overruns its 10s ceiling keeps
running, because interrupt takes no abort signal, so a late session-scoped
abort could stop a turn the user started after the settle was abandoned.
Removing the ceiling is a certain wedge; keeping it is a narrow race needing a
10s+ hang, a new turn inside that window, and the abort still applying. Written
up in the design doc (6c-ii) rather than silently traded.
Two other comments on this head are stale re-posts: the stop_only gating landed
in 86c4c5c (verified present in all seven provider branches), and the
queue-drain in d499220.
* fix: make background-job liveness required and tri-state
Both from this round's review, and both are defects my own liveness fix
introduced one commit earlier.
hasLiveClaudeBackgroundJob was optional, so a wiring that omitted it read a
recorded job as absent and confirmed a clean teardown over work still running —
reopening the exact hole the callback was added to close. Now required.
And it returned a boolean, which collapsed "the daemon says the job is gone"
into the same answer as "the daemon could not be reached"
(getLiveClaudeBackgroundSocket catches socket and request failures and returns
null). Guessing "finished" is the guess that settles over a running job — the
same shape as treating a timed-out liveness read as an idle session, which this
branch has now had to fix three times in three places. It returns
alive / gone / unknown, and only a definite "gone" counts as no work.
Also pinned: interrupt's daemon stop branch is gated on there being no resident
Claude runtime, so a resumed session with a live --bg job takes the SDK branch
and the job survives. Teardown does not claim that as clean — the confirmation
loop still sees the job and reports residue — and there is now a test saying so.
Changing that gate would change what the Stop button does, which is not this
branch's call.
Restored the activeBackgroundTaskCount JSDoc my insertion had detached.
* fix: scope the abort check to the window the teardown actually owns
Codex, and it is the mirror of a fix already made for closing the window.
`end(id, token)` refuses to close a window it does not own, but the abort check
still read `abortedBy(id)` — whatever entry currently occupies the id. When
`deleteSession` runs mid-teardown, `forget` force-closes the entry; if the id is
then recreated and a new settle opens a fresh window, the stale teardown reads
the REPLACEMENT, sees "not aborted", and keeps issuing provider stops against
the new session's work.
`abandoned(id, token)` treats a missing or mismatched entry as abandoned, and
both the in-flight check and the post-await check use it. A settle that no
longer owns its window must stop as surely as one that was aborted.
The other seven comments on this head are threads GitHub re-anchored: stop_only
gating (86c4c5c, verified in all seven provider branches), the queue drain
(d499220), persisted-job liveness and unknown-as-residue (df2aa0a), the
JSDoc association (df2aa0a), the resident-runtime daemon job (pinned as
residue rather than silently clean, with a test), and the un-cancellable
timed-out interrupt, which is documented as a known limitation in 6c-ii because
removing the ceiling trades a narrow race for a certain wedge.
* fix: a hung provider stop is a timeout, not a rejection
Codex. The 10s ceiling set `stopRejected`, so a provider that never answered
was filed identically to one that explicitly refused — in the residue the user
reads AND in the settle_teardown_residue analytics dimension. That conflation
is precisely what the reason field exists to prevent, and it would have made
"how often do stops actually fail in the field" unanswerable, which is the
question 3d option 3 added the event to answer.
Two existing assertions had encoded the bug rather than catching it: both said
a never-resolving interrupt should read "rejected". Corrected, and the fix was
probed against them.1 parent 2617d1d commit 3dd4170
31 files changed
Lines changed: 2302 additions & 287 deletions
File tree
- apps
- ade-cli/src
- desktop/src
- main
- services
- adeActions
- ai/tools
- analytics
- chat
- history
- onboarding
- prs
- sessions
- state
- docs
- features
- sync-and-multi-device
- terminals-and-sessions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
29 | 35 | | |
30 | 36 | | |
31 | 37 | | |
| |||
755 | 761 | | |
756 | 762 | | |
757 | 763 | | |
758 | | - | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
759 | 788 | | |
760 | 789 | | |
761 | 790 | | |
| |||
1249 | 1278 | | |
1250 | 1279 | | |
1251 | 1280 | | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
1252 | 1291 | | |
1253 | 1292 | | |
1254 | 1293 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
85 | 85 | | |
86 | 86 | | |
87 | 87 | | |
| 88 | + | |
| 89 | + | |
88 | 90 | | |
89 | 91 | | |
90 | 92 | | |
| |||
2870 | 2872 | | |
2871 | 2873 | | |
2872 | 2874 | | |
2873 | | - | |
| 2875 | + | |
| 2876 | + | |
| 2877 | + | |
| 2878 | + | |
| 2879 | + | |
| 2880 | + | |
| 2881 | + | |
| 2882 | + | |
| 2883 | + | |
| 2884 | + | |
| 2885 | + | |
| 2886 | + | |
| 2887 | + | |
| 2888 | + | |
| 2889 | + | |
| 2890 | + | |
| 2891 | + | |
| 2892 | + | |
2874 | 2893 | | |
2875 | 2894 | | |
2876 | 2895 | | |
| 2896 | + | |
| 2897 | + | |
| 2898 | + | |
| 2899 | + | |
| 2900 | + | |
| 2901 | + | |
| 2902 | + | |
2877 | 2903 | | |
2878 | 2904 | | |
2879 | 2905 | | |
| |||
3600 | 3626 | | |
3601 | 3627 | | |
3602 | 3628 | | |
| 3629 | + | |
| 3630 | + | |
| 3631 | + | |
| 3632 | + | |
| 3633 | + | |
| 3634 | + | |
| 3635 | + | |
| 3636 | + | |
| 3637 | + | |
| 3638 | + | |
| 3639 | + | |
3603 | 3640 | | |
3604 | 3641 | | |
3605 | 3642 | | |
| |||
Lines changed: 5 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
78 | 83 | | |
79 | 84 | | |
80 | 85 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
734 | 734 | | |
735 | 735 | | |
736 | 736 | | |
| 737 | + | |
737 | 738 | | |
738 | 739 | | |
739 | 740 | | |
| |||
2193 | 2194 | | |
2194 | 2195 | | |
2195 | 2196 | | |
| 2197 | + | |
| 2198 | + | |
| 2199 | + | |
| 2200 | + | |
| 2201 | + | |
| 2202 | + | |
| 2203 | + | |
| 2204 | + | |
| 2205 | + | |
| 2206 | + | |
| 2207 | + | |
| 2208 | + | |
| 2209 | + | |
| 2210 | + | |
2196 | 2211 | | |
2197 | 2212 | | |
2198 | 2213 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
554 | 554 | | |
555 | 555 | | |
556 | 556 | | |
557 | | - | |
| 557 | + | |
558 | 558 | | |
559 | 559 | | |
560 | 560 | | |
| |||
Lines changed: 13 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
93 | 93 | | |
94 | 94 | | |
95 | 95 | | |
96 | | - | |
| 96 | + | |
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
| |||
120 | 120 | | |
121 | 121 | | |
122 | 122 | | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
123 | 127 | | |
124 | 128 | | |
125 | 129 | | |
| |||
132 | 136 | | |
133 | 137 | | |
134 | 138 | | |
135 | | - | |
| 139 | + | |
136 | 140 | | |
137 | 141 | | |
138 | 142 | | |
| |||
183 | 187 | | |
184 | 188 | | |
185 | 189 | | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
186 | 193 | | |
187 | 194 | | |
188 | 195 | | |
| |||
194 | 201 | | |
195 | 202 | | |
196 | 203 | | |
197 | | - | |
| 204 | + | |
198 | 205 | | |
199 | 206 | | |
200 | 207 | | |
201 | 208 | | |
202 | 209 | | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
203 | 213 | | |
204 | 214 | | |
205 | 215 | | |
| |||
Lines changed: 36 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1213 | 1213 | | |
1214 | 1214 | | |
1215 | 1215 | | |
| 1216 | + | |
| 1217 | + | |
| 1218 | + | |
| 1219 | + | |
| 1220 | + | |
| 1221 | + | |
| 1222 | + | |
| 1223 | + | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
| 1230 | + | |
| 1231 | + | |
| 1232 | + | |
| 1233 | + | |
| 1234 | + | |
| 1235 | + | |
| 1236 | + | |
| 1237 | + | |
| 1238 | + | |
| 1239 | + | |
| 1240 | + | |
| 1241 | + | |
| 1242 | + | |
| 1243 | + | |
| 1244 | + | |
| 1245 | + | |
| 1246 | + | |
| 1247 | + | |
| 1248 | + | |
| 1249 | + | |
| 1250 | + | |
| 1251 | + | |
1216 | 1252 | | |
1217 | 1253 | | |
1218 | 1254 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37495 | 37495 | | |
37496 | 37496 | | |
37497 | 37497 | | |
37498 | | - | |
| 37498 | + | |
| 37499 | + | |
| 37500 | + | |
| 37501 | + | |
| 37502 | + | |
| 37503 | + | |
| 37504 | + | |
37499 | 37505 | | |
37500 | 37506 | | |
37501 | 37507 | | |
| |||
37541 | 37547 | | |
37542 | 37548 | | |
37543 | 37549 | | |
37544 | | - | |
| 37550 | + | |
37545 | 37551 | | |
37546 | 37552 | | |
37547 | 37553 | | |
| |||
37554 | 37560 | | |
37555 | 37561 | | |
37556 | 37562 | | |
37557 | | - | |
| 37563 | + | |
37558 | 37564 | | |
37559 | 37565 | | |
37560 | 37566 | | |
37561 | 37567 | | |
37562 | 37568 | | |
37563 | 37569 | | |
37564 | 37570 | | |
37565 | | - | |
| 37571 | + | |
| 37572 | + | |
| 37573 | + | |
37566 | 37574 | | |
37567 | 37575 | | |
37568 | 37576 | | |
| |||
37580 | 37588 | | |
37581 | 37589 | | |
37582 | 37590 | | |
37583 | | - | |
| 37591 | + | |
37584 | 37592 | | |
37585 | 37593 | | |
37586 | 37594 | | |
37587 | 37595 | | |
37588 | 37596 | | |
37589 | | - | |
| 37597 | + | |
| 37598 | + | |
| 37599 | + | |
37590 | 37600 | | |
37591 | 37601 | | |
37592 | 37602 | | |
37593 | 37603 | | |
37594 | 37604 | | |
37595 | 37605 | | |
37596 | | - | |
| 37606 | + | |
| 37607 | + | |
| 37608 | + | |
37597 | 37609 | | |
37598 | 37610 | | |
37599 | 37611 | | |
| |||
44283 | 44295 | | |
44284 | 44296 | | |
44285 | 44297 | | |
| 44298 | + | |
| 44299 | + | |
| 44300 | + | |
| 44301 | + | |
| 44302 | + | |
| 44303 | + | |
| 44304 | + | |
| 44305 | + | |
| 44306 | + | |
| 44307 | + | |
| 44308 | + | |
| 44309 | + | |
| 44310 | + | |
| 44311 | + | |
| 44312 | + | |
| 44313 | + | |
| 44314 | + | |
| 44315 | + | |
| 44316 | + | |
| 44317 | + | |
| 44318 | + | |
| 44319 | + | |
| 44320 | + | |
| 44321 | + | |
| 44322 | + | |
| 44323 | + | |
| 44324 | + | |
| 44325 | + | |
44286 | 44326 | | |
44287 | 44327 | | |
44288 | 44328 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
| 90 | + | |
90 | 91 | | |
91 | 92 | | |
92 | 93 | | |
| |||
0 commit comments