ó
    vºj¢ž ã                   óÚ  • S r SSKrSSKrSSKrSSKrSSKrSSKJrJr	  SSK
Jr  SSKJrJrJrJrJrJrJrJr  SSKrSSKJrJrJrJrJrJrJrJrJrJ r   SSK!J"r"J#r#J$r$J%r%J&r&J'r'J(r(J)r)J*r*  SS	0SS	0SS	0SS	0SS	0SS
0S.r+/ SQr,\, V s0 s H  o S	_M     sn r-S r.S r/\R`                  " SS5      r1\14S jr2S r3S r4SSSSSS.r5\Rl                  Ro                  \Rl                  Rq                  \Rl                  Rs                  \:5      5      SS5      r;\Rl                  Ro                  \Rl                  Rq                  \Rl                  Rs                  \:5      5      SS5      r<\Rl                  Ro                  \Rl                  Rq                  \Rl                  Rs                  \:5      5      SS5      r=\R|                  r?\R€                  rAS rBS rCS rDS  rESÀS! jrFS" rGS# rHSÁS$ jrIS% rJSÁS& jrKS' rLS( rMS) rNS*rOSÁS+ jrP\O4S, jrQS- rRSrSS.rTS/ rUSÁS0 jrVSÁS1 jrWSSSSSS2S3.rXS4 rYSÁS5 jrZS6 r[S7 r\S8 r]S9 r^S: r_S; r`S< raS= rbSqcS> rdS? reS@ rfSA rgSB rhSC riSD rjSE0 SF.rkSG rlSH rmSI rn\Rl                  Ro                  \Rl                  Rq                  \Rl                  Rs                  \:5      5      SSJSK5      ro\Rl                  Ro                  \Rl                  Rq                  \Rl                  Rs                  \:5      5      SSK5      rpSL rqSM rrSN rsSO rtSP ruSQ rvSÂSR jrwSS rxST rySU rzSV r{SÃSW jr|SX r}SY r~SÄSZ jrS[ r€S\ r�SÅS] jr‚SÅS^ jrƒS_ r„S` r…Sa r†Sb r‡SÆSc jrˆSÁSd jr‰Se rŠSfr‹Sg rŒShr�SirŽSÇSj jr�\�SkSl4Sm jr�Snr‘SÈSo jr’SÉSp jr“Sq r”Sr r•Ss r–St r—Su r˜Sv r™Sw ršSx r›SÊSy jrœ\Rl                  Ro                  \Rl                  Rq                  \Rl                  Rs                  \:5      5      SSz5      r�S{ ržSÅS| jrŸS} r S~ r¡S r¢S€ r£S� r¤S‚ r¥Sƒ r¦SËS„ jr§S… r¨S† r©S‡ rªSˆ r«  SÌS‰ jr¬SŠ r­S‹ r®SŒ r¯S� r°SŽ r±S� r²\³S�:X  Ga+  S‘\GRh                  ;   a  \^" 5         gS’\GRh                  ;   a  \I" 5         gS“\GRh                  ;   a  \G" 5         gS”\GRh                  ;   a  \H" 5         gS•\GRh                  ;   a  \GRj                  " S–\€5        gS—\GRh                  ;   a  \GRj                  " S˜\�5        gS™\GRh                  ;   a  \GRj                  " Sš\…5        gS›\GRh                  ;   a  \GRj                  " Sœ\†5        gS�\GRh                  ;   a  \GRj                  " Sž\¤5        gSŸ\GRh                  ;   a  \GRj                  " S \£5        gS¡\GRh                  ;   a  \GRj                  " S¢\¡5        gS£\GRh                  ;   a  \GRj                  " S¤\ 5        gS¥\GRh                  ;   a  \GRj                  " S¦\•5        gS§\GRh                  ;   a  \GRj                  " S¨\”5        gS©\GRh                  ;   a  \GRj                  " Sª\®5        gS«\GRh                  ;   a  \GRj                  " S¬\¥5        gS­\GRh                  ;   a  \GRj                  " S®\¢5        gS¯\GRh                  ;   a  \GRj                  " S°\­5        gS±\GRh                  ;   a  \GRj                  " S²\¬5        gS³\GRh                  ;   a  \GRj                  " S´\§5        gSµ\GRh                  ;   a  \²" 5         gS¶\GRh                  ;   a  \±" 5         gS·\GRh                  ;   a  \¯" 5         gS¸\GRh                  ;   a  \_" 5         gS¹\GRh                  ;   a  \`" 5         gSr¶Sº\GRh                  ;   aR  \GRh                  GRo                  Sº5      r¸\¸S»-   \¹" \GRh                  5      :  a  \º" \GRh                  \¸S»-      5      OSlr¶Sr»S¼\GRh                  ;   a\  \GRh                  GRo                  S¼5      r¸\¸S»-   \¹" \GRh                  5      :  a#  \GRh                  \¸S»-      GRy                  S½5      OSr»\GRj                  " S¾S¿ 5        ggs  sn f )Ía\  
BidBrain daily run.

Reads the live stock off the logged in Motorway and Carwow screens, records
every car for the repeat appearance tracker, applies the gate and bans, reads
each Motorway survivor's own page for previous owners and the exact engine,
looks up Glass's and Cazana retail for the survivors, prices them, and builds
the cockpit page.

It reads screens only. No platform API. It never places a bid.

Run after 5pm or early morning, never between 3:30pm and 5:00pm (see CLAUDE.md).

  python3 daily_run.py                              full run
  python3 daily_run.py --test 3                     only value the 3 cheapest survivors, for a quick check
  python3 daily_run.py --platforms motorway,carwow   only refresh those platforms, carrying the rest
                                                      forward from the last saved run untouched
é    N)ÚasdictÚfields)Úsync_playwright)ÚdbÚrenderÚbrowserÚdealerkit_writeÚfunding_syncÚbid_suggestÚ
comp_shareÚruns)
ÚmotorwayÚcarwowÚauction4carsÚdealerauctionÚ	dealerwayÚglassÚcazanaÚclickdealerÚ	dealerkitÚ	lecapital)	ÚCarÚPricingÚ
AssessmentÚassessÚ
assess_allÚpoundsÚbanned_makeÚbanned_engineÚgate_failuresÚenabledTF)Údealerkit_stockÚdealerkit_purchasesr   Úlecapital_fundingÚdealer_os_pushÚauto_update©r   r   r   r   r   c                 ó¤   • U (       a  [        U 5      O[        [        5      nU Vs1 s H  o1R                  US5      (       d  M  UiM     sn$ s  snf )aê  The real set of platforms a run will actually read. requested is
whatever was asked for (None or empty means every platform, the normal
full run, which is also exactly what the scheduled job always passes),
narrowed down to only the ones switched on in Settings. A platform
turned off there can never be read regardless of what requested it, so
disabling one is a genuine floor, not just a default. Pure function, no
I/O, so it is unit tested directly rather than only through a live
run().T)ÚsetÚPLATFORM_KEYSÚget)Ú	requestedÚenabled_mapÚbaseÚkeys       Ú*/Users/stevendouglas/BidBrain/daily_run.pyÚ_select_platformsr1   W   s9   € ö 'Œ3ˆyŒ>¬C´Ó,>€DÙÓ>š4�C§?¡?°3¸×#=�C™4Ñ>Ð>ùÒ>s   ¦AÁAc                 óp   • [          Vs1 s H  o!R                  US5      (       d  M  UiM     nnX:g  $ s  snf )a[  Whether this run counts as a genuine ad hoc partial refresh, which
reuses the cached sale_date and carries the temporarily-skipped
platforms forward untouched (Mark 2026-08-20). That must only ever be
true of a real narrowing among the platforms this dealership actually
wants read, never of a platform that is simply switched off in Settings
(Mark 2026-08-30: "if a platform is disabled in someones settings it
shouldnt ever effect the runs/shortlist counts"). Comparing selected
against every PLATFORM_KEYS meant a permanently disabled platform made
EVERY run partial forever, which froze sale_date on whatever date it
happened to be when the platform was switched off, however many times
the scheduled job went on to fire successfully afterwards (found live
2026-08-30: Dealer Auction disabled, sale_date stuck 6 days behind
while Motorway and Carwow read fine every single day). Comparing
against only the currently ENABLED keys instead means a run across
everything this dealership still wants is a genuine full run again.
Pure function, no I/O, unit tested directly, same reasoning as
_select_platforms above.T)r*   r+   )Úselectedr-   r/   Úenabled_keyss       r0   Ú_is_partial_runr5   d   s2   € õ$ $1ÓO¢=˜C·O±OÀCÈ×4N—C¡=€LÐOØÑ#Ð#ùò Ps   ‰3§3é   é   c                 ó´   • U R                  5       nU R                  5       U:  a  UR	                  5       $ U[        R                  " SS9-   R	                  5       $ )a(  The sale a run started at `now` is reading for: today's while
today's sale is still open, tomorrow's once it has closed.

Before this a list carried the day the RUN happened. The scheduled run
is at 17:10, after the 15:30 close, so it always read the NEXT sale's
cars and stamped them with yesterday. At midnight Dealer OS then
cleared the board on exactly the cars there were still hours left to
bid on (Steven, 2026-09-10: "you have wiped those cars so i cant even
look at them now before putting my bids in for todays sale at
3.30pm"). Pure, tested.é   ©Údays)ÚdateÚtimeÚdatetimeÚ	timedeltaÚ	isoformat)ÚnowÚends_atÚdays      r0   Úsale_day_forrD   ~   sI   € ð �(‰(‹*€CØ—8‘8“: Ó'ˆC×XÑXÓZÐZ¨S´8×3EÒ3EÈ1Ñ3MÑ-M×XÑXÓZÐZó    c                 ó"   • U (       a  X:¼  a  U $ U$ )a  The sale date a partial (single platform) refresh runs under. The
cached date carries on only while it is today or later, so a refresh
on the same day keeps its stars; a cached date from an EARLIER day
never carries. Before this (Steven 2026-09-08: "those 5 cars were from
yesterdays sale and the shortlist shouldnt carry over") a Motorway
only refresh on Monday ran under Saturday's date because Sunday's
full run had failed, so Saturday's five stars came back on Monday's
list in Dealer OS. Stars belong to their own sale day. Pure, tested.© )Úprev_sale_dateÚtodays     r0   Ú_partial_sale_daterJ   �   s   € ö ˜.Ó1ØÐØ€LrE   c                 ó.   • [        U 5      =(       a    X:¬  $ )a›  Whether the last saved run may lend its cars to a run for sale_date:
only when it is for the same day or later. An earlier day's cars are
never carried into a partial refresh and never used as the fallback
for a platform whose read failed (Steven 2026-09-08, after the stars:
"drop the old cars too"). Those cars were for a sale that has been
and gone; a list for today shows only cars read today. Pure, tested.©Úbool)rH   Ú	sale_dates     r0   Ú_cache_is_currentrO   ›   s   € ô �Ó×? NÑ$?Ð?rE   ÚMotorwayÚCarwowÚAuction4CarsúDealer AuctionÚ	DealerWayÚdatazlast_run.jsonzreview.jsonzrun_progress.jsonc                 óZ   • [        U SS5      =(       d    S[        U SS5      =(       d    S4$ )a%  The plates a car's sightings are filed under: the listing plate, then
the original plate underneath when the seller keeps a private plate
(2026-09-28, S30NNT on Carwow read as never seen because its listing
plate could not be read). db.sighting_plates widens these with any
plate pair on file.ÚregÚ Úselling_vrm)Úgetattr©Úcars    r0   Ú_car_platesr]   º   s+   € ô �C˜ Ó#×) r¬7°3¸ÀrÓ+J×+PÈbÐQÐQrE   c                 ó8  • U R                   c  g[        R                  " [        U 5      U5      nU(       d  gUS   S   nUbY  U R                   U:  aI  SSSS.R	                  U R
                  S5      nU S	[        X0R                   -
  5       S
[        U5       S3$ g)aT  A card line when a car doing the rounds has had its reserve lowered since
it was last seen, a sign the seller is keener. Returns None if no prior
reserve, or if the reserve has not dropped. Carwow, Dealer Auction and
DealerWay neither disclose a real reserve (2026-08-19, 2026-08-20 and
2026-08-24), car.reserve is a stand in for each (CAP Clean, Retail minus
Margin, and Current Bid or Starting Price), so this reads "Trade value
dropped", "AT Trade dropped" or "Bid price dropped" there instead,
matching each card's own gauge label, never presenting a valuation as if
it were the real reserve.Nr   ÚreservezTrade valuezAT Tradez	Bid price)rQ   rS   rT   ÚReservez	 dropped z since last seen (was ú).)r_   r   Úreserve_historyr]   r+   Úsourcer   )r\   rN   ÚhistÚprevÚlabels        r0   Ú_reserve_drop_flagrg   Ã   s¤   € ð ‡{�{ÑØÜ×Òœk¨#Ó.°	Ó:€DÞØØ�‰7�9Ñ€DØÑ˜CŸK™K¨$Ó.Ø(¸JØ)ñ+ß+.©3¨s¯z©z¸9Ó+Eð 	à�'˜¤6¨$·±Ñ*<Ó#=Ð">ð ?Ü˜t›�~ Rð)ð 	*àrE   c           
      óÄ   • S n[        [        SSS9 n[        R                  " X Vs/ s H
  oB" U5      PM     snS.U5        S S S 5        g s  snf ! , (       d  f       g = f)Nc           	      óÞ   • U R                   U R                  [        U R                  5      R	                  5        VVs0 s H  u  pUR                  S5      (       a  M  X_M!     snnS.$ s  snnf )NÚ_)ÚstatusÚflagsr\   )rk   rl   r   r\   ÚitemsÚ
startswith)ÚaÚkÚvs      r0   ÚpackÚ_save_review.<locals>.packÜ   sU   € ØŸ(™(¨Q¯W©WÜ)/°·±«×)<Ñ)<Ô)>ÔXÒ)>¡ ÀaÇlÁlÐSV×FW›˜šÑ)>ÒXñZð 	ZùÛXs   ½A)
ÁA)
Úwúutf-8©Úencoding)rN   Úkeepers)ÚopenÚREVIEW_CACHEÚjsonÚdump)rx   rN   rr   Úfro   s        r0   Ú_save_reviewr~   Û   sS   € òZô 
Œl˜C¨'Ò	2°aÜ�	Š	 	ÈÓ6PÊÀ1°t¸A¶wÉÑ6PÑQÐSTÔU÷ 
3Ð	2ùÚ6P÷ 
3Õ	2ús   “A¨A¹
AÁAÁ
Ac                  ó  • [        [        SS9 n [        R                  " U 5      nS S S 5        WS    Vs/ s H-  n[	        [        S	0 US   D6US   UR                  S/ 5      S9PM/     nnX1S   4$ ! , (       d  f       NQ= fs  snf )
Nru   rv   rx   r\   rk   rl   )r\   rk   rl   rN   rG   )ry   rz   r{   Úloadr   r   r+   )r}   rU   Údrx   s       r0   Ú_load_reviewr‚   ã   s„   € Ü	Œl WÒ	-°Ü�yŠy˜‹|ˆ÷ 
.ð ˜Yšó)Ú'�1ô œc™o A e¡H™o°a¸±kÈÏÉÈwÐXZÓI[Ô\Ù'ð ð )à˜Ñ%Ð%Ð%÷	 
.Õ	-üò)s   �A1µ4BÁ1
A?c           
      óz  • U Vs/ s H[  nUR                   R                  S:X  d  M  UR                   R                  (       a  M<  UR                   R                  (       d  MY  UPM]     nnU(       d  g[	        US[        U5       S3S[        U5      5        [        R                  " S5      n[        R                  " S5      nSn[        R                  " U SSS	9n UR                  (       a  UR                  S   OUR                  5       n	[        US
5       HÌ  u  p£ U	R                  UR                   R                  SSS9  U	R                  SSSS9  U	R                  SS5      nUR!                  U5      =(       d    UR!                  U5      nU(       a(  UR#                  S5      S-   UR                   l        US
-  n[	        USU
 S[        U5       3U
[        U5      5        MÎ     UR'                  5         [)        SU S[        U5       S35        U$ s  snf ! [$         a     Nhf = f! UR'                  5         f = f)a(  Motorway photos are not in the CSV export, so fetch each car's primary
photo from its own page. items is a list of assessments; updates car.photo_url
in place. One browser context, reused, then closed (which also closes its
browser thanks to the leak fix). Carwow cars already carry a card photo.rP   r   z	Fetching z Motorway photosúOhttps://[A-Za-z0-9._\-]*imgix[A-Za-z0-9._/\-]*exterior[A-Za-z0-9._/\-]*?\.jpe?gú6https://[A-Za-z0-9._\-]*imgix[A-Za-z0-9._/\-]*?\.jpe?gr   T©Úheadlessr9   Údomcontentloadedé@œ  ©Ú
wait_untilÚtimeoutú#__NEXT_DATA__Úattachedé'  ©ÚstaterŒ   úel=>el.textContentú?w=640&auto=format,compresszPhoto ú of ú  Ú/z Motorway photos fetched)r\   rc   Ú	photo_urlÚlisting_urlÚ	_progressÚlenÚreÚcompiler   Úopen_reader_contextÚpagesÚnew_pageÚ	enumerateÚgotoÚwait_for_selectorÚeval_on_selectorÚsearchÚgroupÚ	ExceptionÚcloseÚprint)Úprm   Úphasero   ÚneedÚext_reÚany_reÚdoneÚctxÚpageÚiÚblobÚms                r0   Ú_fetch_motorway_photosr´   ë   sé  € ñ
 ó Y’u�!Ø�u‰u�|‰|˜zÑ)ó Ø23·%±%·/µ/ó ØFGÇeÁe×FWÕFW÷ ‘u€Dð YæØÜˆe�y¤ T£ Ð+;Ð<¸aÄÀTÃÔKÜ�ZŠZÐjÓk€FÜ�ZŠZÐQÓR€FØ€DÜ
×
%Ò
% a¨¸dÑ
C€CðØ"ŸyŸyˆs�y‰y˜Š|¨c¯l©l«nˆÜ˜d AÖ&‰DˆAð	Ø—	‘	˜!Ÿ%™%×+Ñ+Ð8JÐTY�	ÑZØ×&Ñ&Ð'7¸zÐSXÐ&ÑYØ×,Ñ,Ð-=Ð?SÓT�Ø—M‘M $Ó'×>¨6¯=©=¸Ó+>�ÞØ&'§g¡g¨a£jÐ3PÑ&P�A—E‘E”OØ˜A‘I�Dô �e˜v a S¨¬S°«Y¨KÐ8¸!¼SÀ»YÖGñ 'ð 	�	‰	ŒÜ	ˆBˆtˆf�A”c˜$“i�[Ð 8Ð
9Ô:Ø€Kùò5Yøô( ó Ùðûð 	�	‰	�úsH   …H§HÁHÁ!HÃAH( ÄB HÆ<*H( È
H%È"H( È$H%È%H( È(H:c                  ó$  • [        5       u  pp#[        R                  R                  U5      n[	        5        n[        XPU-   5        SSS5        [        XX#5        [        XX$U5        [        SSSS5        [        S5        g! , (       d  f       NA= f)zÚBackfill Motorway photos onto the last valued run and rewrite the cockpit,
without re reading or re valuing. Used to fill photos on a run that finished
before the photo step existed (python3 daily_run.py --photos-now).Nr®   zPhotos updated.r9   z(Photos backfilled and cockpit rewritten.)
Ú_load_cacher>   r<   Úfromisoformatr   r´   Ú_save_cacheÚ_write_pagesr™   r¨   )Ú	shortlistÚheldÚrejectedrN   rI   r©   s         r0   Úadd_photos_nowr½     su   € ô ,7«=Ñ(€I�XÜ�M‰M×'Ñ'¨	Ó2€EÜ	Ô	˜aÜ˜q¨dÑ"2Ô3÷ 
ä�	 Ô5Ü� (°9Ô=ÜˆfÐ'¨¨AÔ.Ü	Ð
4Õ5÷ 
Õ	ús   ·BÂ
Bc                  ó:  • [        5       u  p[        R                  R                  U5      nU  Vs/ s H[  nUR                  R
                  S:X  d  M  UR                  R                  (       a  M<  UR                  R                  (       d  MY  UPM]     nn[        S[        U5       S35        [        R                  " S5      n[        5        n[        R                  " USSS9nUR                  (       a  UR                  S   OUR!                  5       nSn	[#        US	5       HÕ  u  p£ UR%                  UR                  R                  S
SS9  UR'                  SSSS9  UR)                  SS5      n[        R*                  " SU5      nU(       d  UR+                  U5      nU(       a(  UR-                  S5      S-   UR                  l        U	S	-  n	U
S-  S:X  d  M·  [        SU
 S[        U5       SU	 S35        M×     UR1                  5         SSS5        [3        X5        [5        SSSS9 nUR7                  [8        R:                  " X5      5        SSS5        [        SW	 S35        gs  snf ! [.         a     Nªf = f! , (       d  f       Nw= f! , (       d  f       ND= f) zÎFetch the primary photo for each Motorway keeper from its page (the CSV
export has no image), update the review cache and rebuild the review page.
Carwow keepers already carry a photo from the listing card.rP   zFetching photos for z( Motorway cars (Carwow already has them)r…   r   Tr†   r   r9   rˆ   r‰   rŠ   r�   rŽ   r�   r�   r’   r„   r“   é   r•   r–   ú (z photos so far)Núcockpit.htmlrt   ru   rv   zDone. z# photos added. Review page rebuilt.)r‚   r>   r<   r·   r\   rc   r—   r˜   r¨   rš   r›   rœ   r   r   r�   rž   rŸ   r    r¡   r¢   r£   r¤   r¥   r¦   r§   r~   ry   Úwriter   Úrender_review)rx   rN   rI   ro   r«   Úimg_rer©   r¯   r°   r®   r±   r²   r³   r}   s                 r0   Ú
add_photosrÅ     s,  € ô &›Ñ€GÜ�M‰M×'Ñ'¨	Ó2€EÙó Y’w�!Ø�u‰u�|‰|˜zÑ)ó Ø23·%±%·/µ/ó ØFGÇeÁe×FWÕFW÷ ‘w€Dð Yä	Ð ¤ T£ Ð+SÐ
TÔUÜ�ZŠZÐQÓR€FÜ	Ô	˜aÜ×)Ò)¨!¨ZÀ$ÑGˆØ"ŸyŸyˆs�y‰y˜Š|¨c¯l©l«nˆØˆÜ˜d AÖ&‰DˆAðØ—	‘	˜!Ÿ%™%×+Ñ+Ð8JÐTY�	ÑZØ×&Ñ&Ð'7¸zÐSXÐ&ÑYØ×,Ñ,Ð-=Ð?SÓT�Ü—I’IÐpÐrvÓw�ÞØŸ™ dÓ+�AÞØ&'§g¡g¨a£jÐ3PÑ&P�A—E‘E”OØ˜A‘I�Dð �2‰v˜�{Ü˜˜1˜#˜Qœs 4›y˜k¨¨D¨6°ÐAÖBñ 'ð 	�	‰	Œ÷' 
ô( �Ô$Ü	ˆn˜c¨GÒ	4¸Ø	�‰”×$Ò$ WÓ4Ô5÷ 
5ä	ˆF�4�&Ð;Ð
<Õ=ùò7Yøô& ó Ùðú÷ 
Õ	ú÷* 
5Õ	4ús[   °I&ÁI&Á/I&ÂI&ÃAI;Ä'B&I+ÇI;Ç2I;È(&JÉ+
I8É5I;É7I8É8I;É;
J	Ê
Jc                 óÄ  • [         R                  " 5         U =(       d,    [        R                  R	                  5       R                  5       n [        R                  R                  U 5      n[        SU  S35        [        5        n[        X 5      n[        X 5      =(       d    / n[        X 5      =(       d    / n[        X 5      =(       d    / n[        X 5      =(       d    / nSSS5        WW-   W-   W-   W-   nU V	s/ s H  n	[        X‘5      PM     n
n	[        U
5        U
 Vs/ s H  o»R                   S;   d  M  UPM     nnUR#                  S S9  [%        XÀ5        ['        SSS	S
9 nUR)                  [*        R,                  " XÁ5      5        SSS5        [        S[/        U5       S35        [        S5        g! , (       d  f       NÝ= fs  sn	f s  snf ! , (       d  f       NM= f)zÓRead and gate both platforms for tomorrow's sales, but do not value yet.
Build the review page so Steven can tick the cars he would not buy. The
gate passers are cached so the survivors can be valued afterwards.z"BidBrain review run for sale date Ú
N)r»   rº   c                 ó`   • U R                   R                  b  U R                   R                  $ S$ )Ni Êš;©r\   r_   ©ro   s    r0   Ú<lambda>Úreview.<locals>.<lambda>V  s!   € °·±·±Ñ0I˜qŸu™uŸ}™}ÐVÈwÐVrE   ©r/   rÁ   rt   ru   rv   z5 cars passed the gate and bans across both platforms.zTReview page written to cockpit.html. Tick the ones to drop, then paste the box back.)r   Úinit_dbr>   r<   rI   r@   r·   r¨   r   Ú_read_motorwayÚ_read_carwowÚ_read_auction4carsÚ_read_dealerauctionÚ_read_dealerwayÚ_assessÚ_tidy_reasonsrk   Úsortr~   ry   rÂ   r   rÃ   rš   )rN   rI   r©   Úmw_carsÚcw_carsÚa4c_carsÚda_carsÚdw_carsÚall_carsÚcÚassessmentsro   rx   r}   s                 r0   Úreviewrß   ?  s{  € ô ‡J‚J„LØ×>œXŸ]™]×0Ñ0Ó2×<Ñ<Ó>€IÜ�M‰M×'Ñ'¨	Ó2€EÜ	Ð.¨y¨k¸Ð
<Ô=Ü	Ô	˜aÜ  Ó.ˆô ˜qÓ,×2°ˆÜ% aÓ3×9°rˆÜ% aÓ3×9°rˆÜ! !Ó/×5°2ˆ÷ 
ð ˜Ñ  8Ñ+¨gÑ5¸Ñ?€HÙ.6Ó7ªh¨”7˜1Ö$©h€KÐ7Ü�+ÔÙ%ÓKš+�Q¯©Ð5JÑ)J�q™+€GÐKØ‡L�LÑV€LÑWÜ�Ô$Ü	ˆn˜c¨GÒ	4¸Ø	�‰”×$Ò$ WÓ4Ô5÷ 
5ä	ˆBŒs�7‹|ˆnÐQÐ
RÔSÜ	Ð
`Õa÷) 
Õ	üò 8ùâK÷ 
5Õ	4ús+   ÂAF6Ã9GÄGÄ6GÅ$&GÆ6
GÇ
Gc           
      óŒ  • S nU[         R                   R                  5       R                  SS9U  Vs/ s H
  oT" U5      PM     snU Vs/ s H
  oT" U5      PM     snU Vs/ s H
  oT" U5      PM     snS.n[        [        SSS9 n[
        R                  " Xg5        SSS5        gs  snf s  snf s  snf ! , (       d  f       g= f)	zpSave the run so the page can be re rendered instantly without re valuing,
handy while tuning the cockpit design.c           
      óN  • U R                   U R                  U R                  [        U R                  5      R                  5        VVs0 s H  u  pUR                  S5      (       a  M  X_M!     snnS.nU R                  (       a  [        U R                  5      OS US'   U$ s  snnf )Nrj   )rk   Úreasonsrl   r\   Úpricing)rk   râ   rl   r   r\   rm   rn   rã   )ro   rp   rq   r�   s       r0   rr   Ú_save_cache.<locals>.packa  sy   € Ø—x‘x¨A¯I©IÀÇÁÜ&,¨Q¯U©U£m×&9Ñ&9Ô&;ÔUÒ&;™d˜aÀ1Ç<Á<ÐPS×CT“T�Q’TÑ&;ÒUñWˆà,-¯I¯I”v˜aŸi™iÔ(¸4ˆˆ)‰Øˆùó Vs   ÁB!Á(B!Úseconds©Útimespec)rN   Úran_atrº   r»   r¼   rt   ru   rv   N)r>   rA   r@   ry   ÚCACHEr{   r|   )rº   r»   r¼   rN   rr   ro   rU   r}   s           r0   r¸   r¸   ^  s­   € òð
 #ô ×'Ñ'×+Ñ+Ó-×7Ñ7ÀÐ7ÐKÙ+4Ó5ª9 a˜$˜qž'©9Ñ5Ù&*Ó+¢d �T˜!–W¡dÑ+Ù*2Ó3ª( Q˜˜až©(Ñ3ñ5€Dô 
Œe�S 7Ò	+¨qÜ�	Š	�$Ô÷ 
,Ð	+ùò 6ùÚ+ùÚ3ß	+Õ	+ús   ³B&Á
B+
Á!B0ÂB5Â5
Cc                 óH  • U R                  S5      =(       d    SnU R                  S5      (       a  U$  [        R                  R                  [        R                  R                  U=(       d    [        5      5      n[        U[        U5      5      $ ! [         a    Us $ f = f)aÐ  The sale day a saved run is really for. Since v3.14.65 the run
works this out itself and the file says when it ran, so its own stamp
stands. A file saved before that carries the day the run happened,
which is a day early whenever the run was after the 15:30 close, so
it is worked out from the file's own time instead. Never moves a day
backwards, and the next save writes the corrected day, so this heals
the one list already on disk and then never applies again.rN   rX   rè   )
r+   r>   ÚfromtimestampÚosÚpathÚgetmtimeré   ÚOSErrorÚmaxrD   )rU   rí   ÚstoredÚrans       r0   Ú_cache_sale_dayró   q  sƒ   € ð �X‰X�kÓ"×( b€FØ‡x�x�×ÑØˆðÜ×Ñ×-Ñ-¬b¯g©g×.>Ñ.>¸t¿}ÄuÓ.MÓNˆô ˆv”| CÓ(Ó)Ð)øô ó ØŠðús   ´A	B ÂB!Â B!c                  óº  ^• [        [        SS9 n [        R                  " U 5      nS S S 5        [	        [
        5       V s1 s H  o R                  iM     sn mU4S jnWS    Vs/ s H
  o2" U5      PM     snUS    Vs/ s H
  o2" U5      PM     snUS    Vs/ s H
  o2" U5      PM     sn[        U5      4$ ! , (       d  f       N—= fs  sn f s  snf s  snf s  snf )Nru   rv   c           
      ó2  >• [        [        S0 U S   D6U S   U R                  S/ 5      U R                  S/ 5      S9nU R                  S5      (       a>  [        S0 U S   R	                  5        VVs0 s H  u  p#UT;   d  M  X#_M     snnD6Ul        U$ s  snnf )Nr\   rk   râ   rl   )r\   rk   râ   rl   rã   rG   )r   r   r+   r   rm   rã   )r�   ro   rp   rq   Úpricing_fieldss       €r0   ÚunpackÚ_load_cache.<locals>.unpack‡  s’   ø€ Üœ3™?  5¡™?°1°X±;Ø Ÿu™u Y°Ó3¸1¿5¹5ÀÈ"Ó;MñOˆà�5‰5�×Ñô  Ña°A°i±L×4FÑ4FÔ4HÔ"`Ò4H©D¨AÈAÐQ_ÑL_£4 1¢4Ñ4HÒ"`ÑaˆAŒIØˆùó #as   Á0BÂ Brº   r»   r¼   )ry   ré   r{   r€   Údataclass_fieldsr   Únameró   )r}   rU   r÷   Úxrö   s       @r0   r¶   r¶   ƒ  sÁ   ø€ Ü	Œe˜gÒ	&¨!Ü�yŠy˜‹|ˆ÷ 
'ä&6´wÔ&?Ó@Ò&? —f”fÑ&?Ñ@€Nõð !% [Ò 1Ó2Ò 1˜1ˆV�AŽYÑ 1Ñ2Ø $ V¢Ó-¢˜1ˆV�AŽY¡Ñ-Ø $ ZÒ 0Ó1Ò 0˜1ˆV�AŽYÑ 0Ñ1Ü˜DÓ!ð#ð #÷ 
'Õ	&üâ@ùò 3ùÚ-ùÚ1s#   �B8Á C	Á%CÁ?CÂCÂ8
Cc                 óÆ   • U(       d  g[         R                  " U R                  U R                  5      nU(       d  gUR	                  US5      nUS:X  a  gUS::  a  SU4$ SU4$ )z÷How this model sits against Steven's current stock, by the shared make
plus model key. gap = none in stock (strong buy signal), ok = 1 to 3,
too_many = 4 or more. None when the model is unknown or no snapshot exists.
Annotation only, never a gate.©NNr   )Úgapr   é   ÚokÚtoo_many)r   Ú	model_keyÚmakeÚmodelr+   )r\   Úcountsr/   Úns       r0   Ú_stock_flagr  –  s_   € ö
 ØÜ
×
Ò
 §¡¨#¯)©)Ó
4€CÞØØ�
‰
�3˜Ó€AØˆAƒvØØˆAƒvØ�aˆyÐØ˜ˆ?ÐrE   c                 óî  • U(       a  UR                  S5      (       d  gUR                  S/ 5       VVs0 s H  u  p#UR                  5       U_M     nnnUR                  U R                  =(       d    SR                  5       R                  5       S5      nUS-  n[        R
                  " U R                  U R                  5      nUR                  S/ 5       HR  u  pƒUR                  SS	5      n	[        U	5      S:X  d  M(  [        R
                  " U	S   U	S	   5      U:X  d  MK  XcS
-  -  n  O   UR                  S5      UR                  S5      UR                  S5      pËn
[        U
5      =(       a:    U R                  SL=(       a%    U
S   U R                  s=:*  =(       a    U
S   :*  Os  n[        U5      =(       a'    U R                  SL=(       a    U R                  US   :*  n[        U5      =(       a:    U R                  SL=(       a%    US   U R                  s=:*  =(       a    US   :*  Os  nU[        U5      [        U5      -   [        U5      -   -  nUS:¬  =(       a    U=(       a    UnUU4$ s  snnf )a   How well a car fits Steven's taste, learned from the cars he has starred.
Returns (match, score): match is a clear "your kind of car" for a badge,
score orders every car for the "Best match for you" sort. Annotation only,
never a gate, it changes nothing about pricing or what is allowed.Útotal)Fr   Úby_makerX   r   é   Úby_modelÚ r9   rÿ   ÚpriceÚmileageÚyearNÚminrð   é   )r+   Úlowerr  Ústripr   r  r  Úsplitrš   rM   r_   r  r  Úint)r\   Úprofiler³   r  ÚmakesÚmk_countÚscoreÚcar_keyÚmsÚpartsÚprÚmlÚyrÚin_priceÚ
in_mileageÚin_yearÚmatchs                    r0   Ú_taste_matchr%  ¨  sò  € ö
 ˜'Ÿ+™+ g×.Ñ.ØØ&-§k¡k°)¸RÔ&@ÔAÒ&@™d˜aˆQ�W‰W‹Y˜Š\Ñ&@€EÑAØ�y‰y˜#Ÿ(™(Ÿ. b×/Ñ/Ó1×7Ñ7Ó9¸1Ó=€HØ�q‰L€Eä×#Ò# C§H¡H¨c¯i©iÓ8€GØ—‘˜Z¨Ö,‰ˆØ—‘˜˜aÓ ˆÜˆu‹:˜�?œ{×4Ò4°U¸1±X¸uÀQ¹xÓHÈGÕSØ˜‘U‰NˆEÙñ	 -ð —‘˜WÓ% w§{¡{°9Ó'=¸w¿{¹{È6Ó?RˆB€BÜ�B‹x×]˜CŸK™K¨tÐ3×]¸¸5¹	ÀSÇ[Á[×8]Ó8]ÐTVÐW\ÑT]Ô8]€HÜ�b“×R˜cŸk™k°Ð5×R¸#¿+¹+ÈÈEÉÑ:R€JÜ�2‹h×V˜3Ÿ8™8¨4Ð/×V°B°u±IÀÇÁ×4VÓ4VÈRÐPUÉYÔ4V€GØ	ŒS�‹]œS ›_Ñ,¬s°7«|Ñ;Ñ;€Eð ˜‰M×5˜h×5¨:€EØ�5ˆ>Ðùó) Bs   ´I1r¿   c           	      óÌ  • SnU(       a‚  UR                  S5      (       al  US   nU[        SSSU-  -   5      -  nUR                  S5      nUb  U[        S[        S	S	U-  S
-  5      5      -  nUR                  S5      nUb  US::  a  US-  nU R                  =(       d    SnU(       a  UR                  OSnUb)  US:”  a#  X‡-
  U-  n	US	[        S[        SU	5      5      -  -  n[        U SS5      n
U
S:X  a  US-  nOU
S:X  a  US-  nOU
S:X  a  US-  n[        U SS5      (       a  US-  nO&U[        S[        U SS5      =(       d    SS-  5      -  n[        U SS5      nUb)  US-  n[        U SS5      (       a  US-  nUb
  X¸::  a  US-  n[        U SS5      (       a  US-  nU R                  =(       d    SR                  5       R                  5       S;   a  US-  n[        US5      $ )u�  One number per shortlisted car for the Top picks view (Steven
2026-09-02, Step 3 of the review: the shortlist showed 116 to 364 cars a
day and he starred 0 to 7, "a shortlist you scroll past isn't doing its
job"). Adds up signals the run already computes, nothing new is read:
room between the reserve and the max bid (as a share of the reserve,
the single strongest buy signal, weighted most), whether this model is
a gap in stock, whether it is the dealer's own kind of car, how much
CompMatch has to say about it, whether it is new since yesterday, and a
small mark down for no service history. Annotation only, never a gate:
every car keeps its own price and status, this only decides which 25
sit at the front. Pure, unit tested on its own.

sold is this model's own learned record (db.learned_for, from the
dealership's real sold cars: n_sold, avg_margin after prep,
avg_days_to_sell). Steven 2026-09-02: "cars i have bought before, i
tend to buy the same cars for a reason, this should be a very strong
signal, as should margin achieved after prep". So a model sold before
is now the single biggest signal (up to 4 points, more the more of them
have sold), the real average margin after prep is next (3 points at
Â£2,000 and above, a model that lost money is marked down), and a fast
seller earns a little extra. A model never sold before scores nothing
here, it is not penalised, the other signals still carry it.ç        Ún_soldg      @g      ø?ç      à?Ú
avg_marginNg       Àg      @g     @Ÿ@Úavg_days_to_sellr7   r   g      ð¿g      ð?Ú_stock_levelrþ   g       @r   r  r%  FÚ_taste_scoreg      $@Ú_suggested_bidÚ_suggested_bid_llkÚ_is_newrX   )ÚnoneÚnozno service historyrÿ   )
r+   r  rð   r_   Úmax_bidrZ   Úservice_historyr  r  Úround)r\   rã   Úsoldr  r  Úmarginr;   r_   r3  ÚroomÚlevelÚsuggs               r0   Ú_pick_scorer;  É  sÿ  € ð. €EÞ�—‘˜×"Ñ"Ø�‰NˆØ”�S˜#  a¡™-Ó(Ñ(ˆØ—‘˜,Ó'ˆØÑØ”S˜œs 3¨¨f©°vÑ(=Ó>Ó?Ñ?ˆEØ�x‰xÐ*Ó+ˆØÑ ¨£
Ø�S‰LˆEØ�k‰k×˜Q€GÞ!(ˆg�oŠo¨d€GØÑ˜w¨›{ØÑ! WÑ,ˆØ�”s˜4¤ S¨$£Ó0Ñ0Ñ0ˆÜ�C˜¨Ó.€EØ�ƒ~Ø�‰‰Ø	�$‹Ø�‰‰Ø	�*Ó	Ø�‰ˆÜˆs�N E×*Ñ*Ø�‰‰à”�Sœ7 3¨¸Ó:×?¸aÀ4ÑGÓHÑHˆÜ�3Ð(¨$Ó/€DØÑØ�‰ˆÜ�3Ð,¨e×4Ñ4Ø�S‰LˆEØÑ 4£?ð �S‰LˆEÜˆs�I˜u×%Ñ%Ø�‰ˆØ×Ñ×!˜r×(Ñ(Ó*×0Ñ0Ó2Ð6ZÓZØ�‰ˆÜ�˜‹?ÐrE   c                 ó¦   • U  H  nSUR                   l        M     [        U S S9n[        USU S5       H  u  pBXBR                   l        M     USU $ )z…Set car._pick_rank (1 for the best) on the top n shortlisted cars by
_pick_score, None on the rest. Ties break on max bid room. Pure.Nc                 ó
  • [        U R                  SS5      =(       d    S* U R                  (       aO  U R                  R                  b8  U R                  R                  U R                  R                  =(       d    S-
  * 4$ S* 4$ )Nr;  r   )rZ   r\   rã   r3  r_   rÊ   s    r0   rË   Ú_rank_picks.<locals>.<lambda>  sm   € ´¸¿¹¸}ÈaÓ0P×0UÐTUÐ.Và45·I·IÀ!Ç)Á)×BSÑBSÑB_ð 34·)±)×2CÑ2CÀqÇuÁuÇ}Á}×GYÐXYÑ2Zð 0hñ.iàefð0hñ.irE   rÍ   r9   )r\   Ú
_pick_rankÚsortedr    )rº   r  ro   Úrankedr±   s        r0   Ú_rank_picksrB  
  s^   € ó ˆØˆ�‰Öñ ä�Iñ $iñ j€Fô ˜&  !˜* aÖ(‰ˆØ�‰Öñ )à�"�1ˆ:ÐrE   c                 óÞ  • UR                  [        R                  " U R                  U R                  5      5      nU(       a  UR                  S5      (       d  gUS   nUR                  S5      nUR                  S5      nUR                  S5      n/ nUb1  [        [        U5      5      nUR                  US:  a  SOSU S	35        Ub  UR                  S
[        U5       35        UbD  US:  a   UR                  S[        U* 5       S35        OUR                  S[        U5       S35        SR                  U5      n	US:”  a  SU S3OSn
U	(       a  U
 SU	R                  5        S3$ U
 S3$ )z‹The model's track record from Really Easy Car Credit's own sold data,
one line for the card. None when this model has not been sold before.r(  Nr+  Úavg_prep_totalr*  r9   zsold same dayzaverage z days to sellzprep r   zlost z after prepzmargin ú, úSold z of these beforezSold 1 of these beforez. Ú.)r+   r   r  r  r  r  r5  Úappendr   ÚjoinÚ
capitalize)r\   ÚlearnedÚrecr  r;   Úprepr7  r  r�   ÚdetailÚheads              r0   Ú_history_flagrP    sV  € ð �+‰+”k×+Ò+¨C¯H©H°c·i±iÓ@Ó
A€CÞ�c—g‘g˜h×'Ñ'ØØˆH‰€AØ�7‰7Ð%Ó&€DØ�7‰7Ð#Ó$€DØ�W‰W�\Ó"€FØ€EØÑÜ”�d“ÓˆØ�‰¨¨A«‘_°X¸a¸SÀÐ3NÔOØÑØ�‰�uœV D›\˜NÐ+Ô,ØÑà�A‹:Ø�L‰L˜5¤¨¨£Ð 1°Ð=Õ>à�L‰L˜7¤6¨&£>Ð"2°+Ð>Ô?Ø�Y‰Y�uÓ€FØ*+¨a«%ˆU�1�#Ð%Ñ&Ð5M€DÞ06ˆdˆV�2�f×'Ñ'Ó)Ð*¨!Ð,ÐF¸t¸fÀA¸JÐFrE   é   c           	      óØ  • X-   nU Vs/ s H  o3R                   (       d  M  UPM     nn/ nU(       a  U(       d  UR                  S5        U$ U(       Ga  [        U5      n[        S U 5       5      n[        S U 5       5      n[        S U 5       5      n	[        S U 5       5      n
[	        S5      nUS:X  a  UR                  S5        U$ US:X  a%  U
(       a  U(       a  UR                  S	U S
35        U$ U
(       a$  U(       a  UR                  SU SU SU
 S35        U$ U	(       a  UR                  SU SU SU	 S35        U$ UR                  SU SUS:X  a  SOS S35        U$ s  snf )a8  Banners derived from the actual last run, so the cockpit shows the state
of play the moment Steven opens it. The scheduled run prices on Cazana, so a
missing Glass's value is normal (a prompt to run the Glass's checks), while a
missing Cazana value across the whole run means Cazana is down and needs a
re login.zxNo cars could be priced in the last run. Cazana looks down. Log in again from Settings > Integrations to price the list.c              3   óV   #   • U  H  oR                   R                  c  M  Sv •  M!     g 7f©Nr9   )r\   Úglass_retail©Ú.0ro   s     r0   Ú	<genexpr>Ú*_session_health_notices.<locals>.<genexpr>K  s   é € ÐJ¢˜A¯5©5×+=Ñ+=—a‘a¢ùó   ‚) 	)c              3   óV   #   • U  H  oR                   R                  c  M  Sv •  M!     g 7frT  )r\   Úcazana_retailrV  s     r0   rX  rY  L  s   é € ÐL¢&˜Q¯E©E×,?Ñ,?—q‘q¢&ùrZ  c              3   ó’   #   • U  H=  nUR                   R                  b  M  UR                   R                  (       d  M9  Sv •  M?     g 7frT  ©r\   rU  Úglass_checkedrV  s     r0   rX  rY  O  s7   é € ð M¢˜AØŸ™×+Ñ+ó Ø89¿¹×8KÕ8K÷ ‘a¢ùó   ‚A¡A¾	Ac              3   ó’   #   • U  H=  nUR                   R                  b  M  UR                   R                  (       a  M9  Sv •  M?     g 7frT  r^  rV  s     r0   rX  rY  Q  s7   é € ð Q¢˜AØŸ™×+Ñ+ó Ø<=¿E¹E×<OÕ<O÷ ‘a¢ùr`  r   r   zjCazana was not read in the last run, so cars are priced on Glass's alone. Re login Cazana when convenient.zNone of these zc cars have a Glass's value yet, they are priced on Cazana. Click Run Glass's checks to add Glass's.zGlass's added to r”   z cars so far. zt still to check, the pass may have stopped early. Click Run Glass's checks again to continue from where it left off.z cars. The other zw could not be matched on Glass's, so they stay priced on Cazana. This is normal for private plates and unclear engines.zAll r  r9   zcar hasz	cars havez$ both Glass's and Cazana valuations.)rã   rH  rš   ÚsumÚ_integration_enabled)rº   r»   Úshownro   ÚpricedÚnotesr  Úglass_nÚcazana_nÚnomatchÚpendingÚglass_ons               r0   Ú_session_health_noticesrl  <  s¶  € ð Ñ€EÙÓ,š�A§)¥)�a™€FÐ,Ø€EÞ–VØ�‰ð Tô 	UàˆßÜ�‹KˆÜÑJ¡ÓJÓJˆÜÑL¡&ÓLÓLˆô ñ M¡ó Mó Mˆäñ Q¡ó Qó Qˆô (¨Ó0ˆØ�q‹=Ø�L‰Lð Nô Oð$ €Lð! ˜‹\žgÞØ—‘˜~¨a¨Sð 1Zð [ô \ð €Lö ÞØ—‘Ð0°°	¸¸a¸SÀÈwÈið XXð Yô Zð €Lö Ø�L‰LÐ,¨W¨I°T¸!¸Ð<MÈgÈYð WZð [ô \ð €Lð �L‰L˜4 ˜s !°°a³¡I¸[Ð#Ið J2ð 3ô 4à€LùòS -s
   ‰E'¡E'c                 ó:  • SSK nSSKn[        R                  " S5      n UR                  R                  U5      nU b  U OUR                  5       n [        [        X-
  S-  5      -
  nUS:  a  gU[        ::  a  SU SUS	:X  a  S
 S3$ S S3$ g! [         a     gf = f)zÚA countdown warning when the Glass's login is inside 5 days of its 30
day expiry, so Steven can renew before valuations start failing. Reads the
age of the saved session state. None when there is nothing to warn about.r   Nr   z?Glass's has no saved login. Valuations will run on Cazana only.i€Q zMGlass's login has likely expired. Renew it or cars will price on Cazana only.zGlass's login expires in about r  r9   rC   r;   z+. Renew it to keep both valuations running.)
rì   r=   r   Ú
state_pathrí   rî   rï   ÚGLASS_SESSION_DAYSr  ÚGLASS_WARN_DAYS)rA   rì   r=   ÚspÚsavedÚ	days_lefts         r0   Ú_glass_login_noticert  o  sÂ   € ÷ Ü	×	Ò	˜GÓ	$€BðQØ—‘× Ñ  Ó$ˆð ‘‰# d§i¡i£k€CÜ"¤S¨#©+¸%Ñ)?Ó%@Ñ@€IØ�1ƒ}ð"à”OÓ#Ø1°)°¸AØ%¨›N�5Ð7ð 8+ð,ð 	-Ø06Ð7ð 8+ð,ð 	-ð øô ó QÙPðQús    B Â
BÂBc                 óT  • [        S5      (       d  gSSKnU =(       d    UR                  R                  5       n[        R
                  " 5       nU(       d  g UR                  R                  U5      nX$-
  R                  S:”  a  SUR                  S5       S3$ g! [         a     gf = f)	zÆA banner when the DealerKit stock read has gone stale (login expired),
so the gap and in stock flags are out of date. None when the snapshot is
recent enough (a healthy setup reads stock every run).r"   Nr   zsNo DealerKit stock has been read, so the gap and in stock flags are missing. Click Run DealerKit stock to add them.r  zStock counts are from z%d %bz{. The DealerKit login may have expired, so the gap and in stock flags are stale. Click Run DealerKit stock to refresh them.)
rc  r>   r<   rI   r   Úlatest_stock_dater·   Ú
ValueErrorr;   Ústrftime)rA   Ú_dtrI   r�   Úsnaps        r0   Ú_stock_health_noticer{  …  s§   € ô  Ð 1×2Ñ2ØÛØ×#�3—8‘8—>‘>Ó#€EÜ
×ÒÓ€AÞðFðØ�x‰x×%Ñ% aÓ(ˆð 	‰×Ñ˜QÓØ(¨¯©°wÓ)?Ð(@ð A3ð 4ð 	5ð øô ó Ùðús   ÁB Â
B'Â&B'ú
LE Capital)r   r   r   r   r   r   c                  ó„  • / n [         R                  " 5       n[        5       R                  S5      =(       d    / n[        R                  5        Hn  u  p4U[        ;   a  [        U5      (       d  M!  X2;   a  M(  UR                  U5      nU(       d  MB  UR                  S5      (       a  MZ  U R                  U S35        Mp     U $ )a  A banner for any platform (the four auction houses plus LE Capital)
whose last real read failed, piggybacking on the runs that already
happen rather than a separate poller checking each site on a timer
(Mark 2026-08-24: "add a notification when its required to relogin").
Deliberately not a background poll: Glass's has repeatedly shown that
even just checking a session can evict the real one when only one
login is allowed account wide (see CLAUDE.md), so nothing here ever
logs into anything on its own, this only reports what a real run
already found. A site with no recorded failure (never read, or its
last read succeeded) says nothing. Cleared automatically the moment
a later read succeeds (db.record_site_health overwrites, not
accumulates). A platform deliberately switched off in Settings is
skipped here too (2026-08-25): its last recorded read may well have
failed (that could be exactly why it was turned off), and nagging to
relogin to something Steven has consciously chosen not to run would be
a false alarm, not a real one.Úhidden_connectionsr   zj could not be read in the last run, the login may have expired. Log in again from Settings > Integrations.)	r   Úget_site_healthÚ_get_settingsr+   Ú_SITE_HEALTH_LABELSrm   r*   Ú_platform_enabledrH  )ÚnoticesÚhealthÚhiddenÚsiterf   Úrows         r0   Ú_platform_health_noticesrˆ  ¢  s§   € ð" €GÜ×ÒÓ!€FÜ‹_× Ñ Ð!5Ó6×<¸"€FÜ*×0Ñ0Ö2‰ˆØ”=Ó Ô):¸4×)@Ñ)@Ùð
 ‹>ÙØ�j‰j˜Óˆßˆ3�s—w‘w˜t—}“}Ø�N‰N˜e˜Wð %Wð Xö Yñ 3ð €NrE   c                 óÒ   ^^• T=(       d
    [        5       mS m[        U 5      [        S U  5       5      [        U4S jU  5       5      [        U5      [        U4S jU  5       5      S.$ )zÎCounts for the filter chips at the top of the cockpit. These are the
initial server side numbers; the page recomputes them per platform tab in
JS, so they stay honest as Steven switches Motorway and Carwow.c                 ó†   • U R                   =(       d    SR                  5       R                  5       nUS:H  =(       d    SU;   $ )NrX   Ú	automaticÚsemi)Útransmissionr  r  )rÝ   Úts     r0   Úis_autoÚ_summary.<locals>.is_autoÌ  s6   € Ø�^‰^×!˜r×(Ñ(Ó*×0Ñ0Ó2ˆØ�KÑ×. 6¨Q¡;Ð.rE   c              3   ó`   #   • U  H$  n[        UR                  S S5      S:X  d  M   Sv •  M&     g7f)r,  Nrþ   r9   )rZ   r\   rV  s     r0   rX  Ú_summary.<locals>.<genexpr>Ñ  s'   é € Ð^¢9˜a´¸¿¹¸~ÈtÓ0TÐX]Ñ0]Ÿ™¢9ùs   ‚.¥	.c              3   óZ   >#   • U  H   nT" UR                   5      (       d  M  S v •  M"     g7f©r9   Nr[   )rW  ro   r�  s     €r0   rX  r’  Ò  s   øé € Ð;šy˜!©G°A·E±E¯N—A‘Ašyùs   ƒ+¢	+c           
   3   ó¸   >#   • U  HO  n[         R                  " TUR                  R                  [	        UR                  S S5      5      (       d  MK  Sv •  MQ     g7f)rc   Nr9   )r   Ústarredr\   rW   rZ   )rW  ro   Úbidsets     €r0   rX  r’  Ô  s=   øé € Ðq¢i ´2·:²:¸fÀaÇeÁeÇiÁiÔQXÐYZ×Y^ÑY^Ð`hÐjnÓQo×3pŸ1™1¢iùs   ƒA
AÁ	A)ÚconsiderÚfill_gapÚautor»   Úshortlisted)r)   rš   rb  )rº   r»   r—  r�  s     `@r0   Ú_summaryrœ  Æ  sY   ù€ ð �_”s“u€Fò/ô ˜	“NÜÑ^¡9Ó^Ó^ÜÔ;™yÓ;Ó;Ü�D“	ÜÔq¡iÓqÓqñð rE   c                 ó®
  ^• [         R                  " 5       mU4S jnU  Vs/ s H  oC" U5      (       d  M  UPM     n nU Vs/ s H  oC" U5      (       d  M  UPM     nn[         R                  " U5      n[         R                  " 5       n[         R                  " 5       (       a  [         R
                  " 5         [         R                  " 5       n[         R                  " 5       n[         R                  " 5       [        R                  " [        [        SS5      S9-   n	[        R                  " U	5      n
X-    GHC  n[        UR                   U5      u  p¼X´R                   l        XÄR                   l        ['        UR                   U5      u  pÞXÔR                   l        XäR                   l        [+        UR                   U5      nU(       a<  [-        S UR.                   5       5      (       d  UR.                  R1                  U5        UR2                  (       a  UR2                  R4                  OSn[        R6                  " UR                   U
US9nU(       a  US   OSUR                   l        U(       a  US	   OSUR                   l        U(       a  US
   OSUR                   l        U(       a  US   OSUR                   l        U(       a  US   OSUR                   l         U(       a  URC                  S/ 5      O/ UR                   l"        [         RF                  " [I        UR                   5      U5      n[K        URC                  S5      5      =(       a    URC                  S5      (       + UR                   l&        URC                  S0 5      URC                  S/ 5      [         RN                  " [I        UR                   5      U5      URC                  [P        RR                  " UR                   RT                  UR                   RV                  5      5      =(       d    SS.UR                   l,        [         RZ                  " XTR                   R\                  [        UR                   SS5      5      UR                   l/        GMF     U  H  n[a        UR                   UR2                  URC                  [P        RR                  " UR                   RT                  UR                   RV                  5      5      5      UR                   l0        M�     [c        U 5        [e        XU5      n[g        X5      n[i        5       nU(       a  UU/-   nU[k        5       -   nXUUU4$ s  snf s  snf )aA  Everything the cockpit page draws beyond the run's own decisions:
drop hidden and walked away cars, pre tick the bid stars, and set the
per car annotations (stock gap, taste match, sold history, CompMatch's
suggestion, repeat history, Top picks rank). Returns (shortlist, held,
bidset, summary, notices).

Pulled out of _write_pages (2026-09-02) so the Dealer OS push can call
the SAME block: before this the on demand "Dealer OS" button sent cars
with none of these annotations and did not apply hides, because only
_write_pages ever set them. One block, both paths, no drift.c                 ó^   >• [         R                  " U R                  R                  5      T;  $ ©N)r   Ú	_norm_regr\   rW   )ro   r…  s    €r0   rË   Ú_annotate.<locals>.<lambda>ä  s   ø€ ”R—\’\ !§%¡%§)¡)Ó,°FÒ:rE   ÚDEALER_NAMErX   )Úexclude_dealerc              3   óB   #   • U  H  oR                  S 5      v •  M     g7f)rF  N)rn   )rW  r}   s     r0   rX  Ú_annotate.<locals>.<genexpr>  s   é € ÐCº7°aŸL™L¨×1Ð1º7ùs   ‚N)Ú
car_retailÚ	suggestedr  Úlike_for_likeFÚratio_basedÚcappedÚcompsÚknownÚpreviousÚsame_day_platforms)r­  Úsame_dayÚreservesr6  rc   )6r   Úsuppressed_regsÚbids_forÚlatest_stock_countsÚlearned_keys_staleÚrebuild_learned_modelsÚlearned_forÚpreference_profileÚbid_comps_fullr   Úload_shared_compsrZ   Údealer_configr   Ú
build_poolr  r\   r,  Ú_stock_countr%  r-  rP  Úanyrl   rH  rã   r\  Úsuggest_bid_for_carr.  Ú_suggested_bid_nr/  Ú_suggested_bid_ratioÚ_suggested_bid_cappedr+   Ú_suggested_bid_compsÚrepeat_summaryr]   rM   r0  rb   r   r  r  r  Ú_historyr–  rW   Ú_starredr;  rB  rœ  rl  r{  rˆ  )rº   r»   rN   Úkeepro   r—  r  rK  ÚtasteÚ	all_compsÚbid_poolr9  r  r$  r  Únoter¦  r:  ÚrepÚsummaryrƒ  Ú
stock_noter…  s                         @r0   Ú	_annotaterÎ  Ø  s½  ø€ ô ×ÒÓ!€FÜ:€DÙ%Ó1šI�q¨¨a¯—™I€IÐ1ÙÓ'’t�!˜t AŸw�A‘t€DÐ'Ü�[Š[˜Ó#€Fô ×#Ò#Ó%€Fô 
×Ò×ÑÜ
×!Ò!Ô#Ü�nŠnÓ€GÜ×!Ò!Ó#€Eô ×!Ò!Ó#¤j×&BÒ&BÜœ}¨m¸RÓ@ñ'Bñ B€Iä×%Ò% iÓ0€HØÕˆÜ˜qŸu™u fÓ-‰ˆØ"�‰ÔØ�‰ÔÜ# A§E¡E¨5Ó1‰ˆØ"�‰ÔØ"�‰ÔÜ˜QŸU™U GÓ,ˆÞœÑC¸1¿7º7ÓC×CÑCØ�G‰G�N‰N˜4Ô Ø01·	·	�Q—Y‘Y×,Ò,¸tˆ
Ü×.Ò.¨q¯u©u°hÈ:ÑVˆÞ48˜t KÒ0¸dˆ�‰ÔÞ.2  c¢¸ˆ�‰ÔÞ<@ 4¨Ò#8Àeˆ�‰Ô Þ<@ T¨-Ò%8Àeˆ�‰Ô"Þ8< d¨8¢nÀ%ˆ�‰Ô#Þ>B T§X¡X¨g°rÔ%:Èˆ�‰Ô"ô ×Ò¤¨A¯E©EÓ 2°IÓ>ˆô ˜SŸW™W WÓ-Ó.×J°s·w±w¸zÓ7JÔ3Jˆ�‰ŒàŸ™ 
¨BÓ/ØŸ™Ð 4°bÓ9Ü×*Ò*¬;°q·u±uÓ+=¸yÓIØ—K‘K¤× 5Ò 5°a·e±e·j±jÀ!Ç%Á%Ç+Á+Ó NÓO×WÐSWñ	
ˆ�‰Œô Ÿš F¯E©E¯I©I´w¸q¿u¹uÀhÐPTÓ7UÓVˆ�‰�ñC óF ˆÜ'Ø�E‰E�1—9‘9˜gŸk™k¬+×*?Ò*?ÀÇÁÇ
Á
ÈAÏEÉEÏKÉKÓ*XÓYó[ˆ�‰Öñ ô �	ÔÜ�y¨Ó/€GÜ% iÓ6€GÜ%Ó'€JÞØ˜Z˜LÑ(ˆØÔ0Ó2Ñ2€GØ˜F G¨WÐ4Ð4ùò[ 2ùÚ's   ¡UµUÁUÁUc                 ó  • [        XU5      u  ppVn[        SSSS9 nUR                  [        R                  " XU40 SU_SS_SS	_S
U_SU_SU_SU_S[        5       _S[        5       _S[        5       R                  S5      _S[        5       R                  S5      _S[        5       _S[        5       R                  S5      _S[        5       R                  S5      _S[        5       R                  S5      _S[        5       R                  S5      _S[        5       R                  S5      _S[        5       R                  S5      _S[        5       R                  S5      _S[        5       R                  S5      _S[        5       R                  S5      _65        SSS5        [        SSSS9 nUR                  [        R                  " U5      5        SSS5        [        U 5      $ ! , (       d  f       NR= f! , (       d  f       N*= f)z¨Write cockpit.html (with hidden cars suppressed and bid stars pre ticked)
and the hidden cars page. Used by both run() and render_only() so the two
paths never diverge.rÁ   rt   ru   rv   rI   Úshow_excludedTÚshow_rejectedFr—  rN   rÌ  rƒ  ÚnoticeÚintegrationsÚdefault_price_ceilingÚdetails_styleÚplatform_enabledr~  Útheme_bgÚtheme_priceÚ
theme_pillÚ
theme_iconÚtheme_sidebarÚtheme_logo_fullÚtheme_logo_smallÚtheme_faviconNzhidden.html)rÎ  ry   rÂ   r   Úrender_pagert  Ú_integrationsr€  r+   Ú_platforms_enabledÚrender_hiddenrš   )	rº   r»   r¼   rI   rN   r—  rÌ  rƒ  r}   s	            r0   r¹   r¹   5  s  € ô 1:¸)È9Ó0UÑ-€I�V gÜ	ˆn˜c¨GÒ	4¸Ø	�‰”×"Ò" 9°Hò WÁEð WÙ15ðWÙEJðWá*0ðWá<EðWáOVðWñ ,3ðWô +>Ô*?ð	Wô
 1>´ðWô :G»×9LÑ9LÐMdÔ9eðWô 2?³×1DÑ1DÀ_Ô1UðWô 5GÔ4HðWô 7D³o×6IÑ6IÐJ^Ô6_ðWô -:«O×,?Ñ,?À
Ô,KðWô 0=«×/BÑ/BÀ=Ô/QðWô /<«o×.AÑ.AÀ,Ô.OðWô /<«o×.AÑ.AÀ,Ô.OðWô 2?³×1DÑ1DÀ_Ô1UðWô 4A³?×3FÑ3FÐGXÔ3YðWô  5B³O×4GÑ4GÐHZÔ4[ð!Wô" 2?³×1DÑ1DÀ_Ô1Uð#Wô 	X÷ 
5ô& 
ˆm˜S¨7Ò	3°qØ	�‰”×$Ò$ UÓ+Ô,÷ 
4äˆy‹>Ð÷+ 
5Õ	4ú÷& 
4Õ	3ús   œE:G"Æ)&G3Ç"
G0Ç3
Hc           	      ó,  • [        U S S9n [        R                  " X-   U-   5        [        XX$5        [	        U 5      [	        U5      -   [	        U5      -   n[        SSXU5        [        XX#U5        [        SS[	        U 5       S[	        U5       S3XUS	S
9  g)a$  Save the cache and rewrite the cockpit with however much of the run has
actually finished so far. Called once after each platform in run()'s own
loop completes (Mark 2026-08-24: cars should be viewable in each auction
as soon as that auction's own scrape and valuing is done, not held back
until every platform in the run has finished), and once more at the very
end, so a slow or hung platform never blocks an already finished one's
results from showing. shortlist is sorted by reserve before saving,
exactly as the final sort in run() always did.c                 ó.   • U R                   R                  $ rŸ  rÉ   rÊ   s    r0   rË   Ú#_write_run_so_far.<locals>.<lambda>[  s   € °·±·²rE   rÍ   ÚwritingzUpdating the cockpit pager®   z	Updated. ú shortlisted, z held so far.T)ÚpartialN)r@  r   Úsave_assessmentsr¸   rš   r™   r¹   )rº   r»   r¼   rI   rN   r  s         r0   Ú_write_run_so_farrê  R  s�   € ô �yÑ&=Ñ>€IÜ×Ò˜	Ñ(¨8Ñ3Ô4Ü�	 Ô5ÜˆI‹œ˜T›Ñ"¤S¨£]Ñ2€AÜˆiÐ4°aÔ;Ü� (°9Ô=ô ˆf˜	¤# i£.Ð!1°ÄÀDÃ	¸{È-ÐXØ˜Dó"rE   c                  ó¾   • [         R                  " 5         [        5       u  pp#[        R                  R                  U5      n[        XX$U5      n[        SU S35        g)z9Rebuild cockpit.html from the last saved run, no network.z,Re rendered cockpit.html from the last run (z( shortlisted after hidden cars removed).N)r   rÎ   r¶   r>   r<   r·   r¹   r¨   )rº   r»   r¼   rN   rI   r  s         r0   Úrender_onlyrì  g  sL   € ä‡J‚J„LÜ+6«=Ñ(€I�XÜ�M‰M×'Ñ'¨	Ó2€EÜ�Y h°yÓA€AÜ	Ð8¸¸Ð;cÐ
dÕerE   c                  óÂ  • [         R                  " 5         [        5       u  pp#[        R                  R                  U5      nX-   U-   nX-    Vs/ s HG  ofR                  R                  S:X  d  M  UR                  R                  (       d  M;  UR                  PMI     nn[        S[        U5       S35        Sn[        5        n	[        R                  " U	SSS9n
 U
R                  (       a  U
R                  S   OU
R                  5       n[!        US5       HX  u  pÍ ["        R$                  " X½5        UR&                  (       a  US-  n[+        S	S
U S[        U5       3U[        U5      5        MZ     U
R-                  5          SSS5        U(       a:  US[        U5      -  :  a(  [+        SS5        [        SU S[        U5       S35        gU Vs/ s H  n[/        UR                  U5      PM     nn[1        U5        U Vs/ s H  ofR2                  S:X  d  M  UPM     nnU Vs/ s H  ofR2                  S:X  d  M  UPM     nnU Vs/ s H  ofR2                  S:X  d  M  UPM     nnUU-    Hî  n[         R4                  " [7        UR                  5      U5      n[         R8                  " U5       H  nUR:                  R=                  U5        M      [?        UR                  U5      nU(       a  UR:                  R=                  U5        [A        UR                  SS5      (       d  M»  UR:                  R=                  SUR                  RB                   S35        Mð     URE                  S S9  [         RF                  " UU-   U-   5        [I        UUUU5        [K        UUUXC5        [+        SS[        U5       S[        U5       S3[        U5      [        U5      5        [        S[        U5       S[        U5       S [        U5       S35        gs  snf ! [(         a     GNÙf = f! U
R-                  5         f = f! , (       d  f       GN¾= fs  snf s  snf s  snf s  snf )!aU  Re read the Motorway detail pages for the cars on the last run and re
assess, applying the keeper date, known issue and private plate checks even
if the original run could not read those pages. Drops any car that now fails
a hard rule and rewrites the cockpit, keeping the existing valuations so the
Glass's work is not lost (no re valuing).rP   zRe reading details for z Motorway cars on the last runr   r   Tr†   r9   ÚreadingzRe reading Motorway pages, r”   Nr)  ÚerrorzVCould not read Motorway car details, nothing changed. Re login Motorway and try again.zDetail read failed (z% got a keeper date). Nothing changed.rº   r»   r¼   Ú_valued_on_opFú=Listing reg is a private plate. Valued on the original plate ú, details matched.c                 ó.   • U R                   R                  $ rŸ  rÉ   rÊ   s    r0   rË   Ú!recheck_details.<locals>.<lambda>¤  ó   €  §¡§¢rE   rÍ   r®   zRe checked details. Shortlist ú, held rG  z
Done. Shortlist now ú, rejected )&r   rÎ   r¶   r>   r<   r·   r\   rc   r˜   r¨   rš   r   r   r�   rž   rŸ   r    r   Úgate_detailsÚkeeper_startr¦   r™   r§   rÔ   rÕ   rk   rÃ  r]   Úrepeat_flag_linesrl   rH  rg   rZ   rY   rÖ   ré  r¸   r¹   )rº   r»   r¼   rN   rI   Úcarsro   ÚmwÚread_okr©   r¯   r°   r±   rÝ   ÚvaluedÚ	new_shortÚnew_heldÚnew_rejÚsummÚlineÚdrops                        r0   Úrecheck_detailsr  p  s›  € ô ‡J‚J„LÜ+6«=Ñ(€I�XÜ�M‰M×'Ñ'¨	Ó2€Eð
 Ñ˜hÑ&€DØ#Ò*Ó	`Ò*�A¯u©u¯|©|¸zÑ/I‹%ÈaÏeÉe×N_ÕN_‹%ˆ!�%Œ%Ñ*€BÐ	`Ü	Ð#¤C¨£G 9Ð,JÐ
KÔLØ€GÜ	Ô	˜aÜ×)Ò)¨!¨ZÀ$ÑGˆð	Ø#&§9§9�3—9‘9˜Q’<°#·,±,³.ˆDÜ! " aÖ(‘�ðÜ×)Ò)¨$Ô2Ø—~—~Ø 1™˜ô ˜)Ð'BÀ1À#ÀTÌ#ÈbË'ÈÐ%SÐUVÔX[Ð\^ÓX_Ö`ñ )ð �I‰I�K÷ 
ö 
ˆg˜œc "›g™Ó%Ü�'ð 5ô 	6äÐ$ W I¨T´#°b³'°Ð:_Ð`ÔaØá-1Ó2ªT¨Œg�a—e‘e˜UÖ#©T€FÐ2Ü�&ÔÙ"Ó>šF�q§h¡h°+Ñ&=—™F€IÐ>Ù!Ó8š6�a§X¡X°Ñ%7—™6€HÐ8Ù Ó;š&�Q§H¡H°
Ñ$:�q™&€GÐ;Ø˜Ô!ˆÜ× Ò ¤¨Q¯U©UÓ!3°YÓ?ˆÜ×(Ò(¨Ö.ˆDØ�G‰G�N‰N˜4Ö ñ /ä! !§%¡%¨Ó3ˆÞØ�G‰G�N‰N˜4Ô Ü�1—5‘5˜/¨5×1Ó1Ø�G‰G�N‰NÐZØŸe™e×/Ñ/Ð0Ð0BðDö Eñ "ð ‡N�NÑ.€NÑ/Ü×Ò˜	 HÑ,¨wÑ6Ô7Ü�	˜8 W¨iÔ8Ü�˜H g¨uÔ@ÜˆfÐ6´s¸9³~Ð6FÀgÌcÐRZËmÈ_Ð\]Ð^Ü�"‹g”s˜2“wô ä	Ð"¤3 y£>Ð"2°'¼#¸h»-¸ð IÜ˜'“l�^ 1ð&õ 'ùòY 
aøô !ó Úðûð �I‰I�Kú÷ 
Ö	üò( 3ùâ>ùÚ8ùÚ;s…   ÁPÁ0PÂPÃP;ÃAP&Ä,PÅ	*P&Å3P;Ç QÈQÈQÈ%QÈ<QÉQÉQÐ
P#ÐP&Ð"P#Ð#P&Ð&P8Ð8P;Ð;
Q
c            
      ó&  • [         R                  " 5         [        5       u  pp#[        R                  R                  U5      nX-   U-   nU Vs/ s H  n[        UR                  U5      PM     nn[        U5        U Vs/ s H  ofR                  S:X  d  M  UPM     nnU Vs/ s H  ofR                  S:X  d  M  UPM     n	nU Vs/ s H  ofR                  S:X  d  M  UPM     n
nX‰-    HŸ  n[         R                  " [        UR                  5      U5      n[         R                  " U5       H  nUR                  R                  U5        M      [        UR                  U5      nU(       d  M„  UR                  R                  U5        M¡     UR!                  S S9  [         R"                  " X‰-   U
-   5        [%        X‰X£5        ['        X‰X¤U5        [)        S[+        U5       S[+        U	5       S[+        U
5       S	35        g
s  snf s  snf s  snf s  snf )a  Re run pricing on every car from the last saved run, using the
valuations already read (no network calls, no photos re fetched). For
when a pricing rule changes (for example the Cazana cap removal) and the
values need updating without re reading the platforms.rº   r»   r¼   c                 ó.   • U R                   R                  $ rŸ  rÉ   rÊ   s    r0   rË   Úreprice.<locals>.<lambda>Ä  rõ  rE   rÍ   z7Repriced from the last run, nothing re read. Shortlist rö  r÷  rG  N)r   rÎ   r¶   r>   r<   r·   rÔ   r\   rÕ   rk   rÃ  r]   rú  rl   rH  rg   rÖ   ré  r¸   r¹   r¨   rš   )rº   r»   r¼   rN   rI   rû  ro   rþ  rÿ  r   r  r  r  r  s                 r0   Úrepricer	  ®  s©  € ô
 ‡J‚J„LÜ+6«=Ñ(€I�XÜ�M‰M×'Ñ'¨	Ó2€EØÑ˜hÑ&€Dá-1Ó2ªT¨Œg�a—e‘e˜UÖ#©T€FÐ2Ü�&ÔÙ"Ó>šF�q§h¡h°+Ñ&=—™F€IÐ>Ù!Ó8š6�a§X¡X°Ñ%7—™6€HÐ8Ù Ó;š&�Q§H¡H°
Ñ$:�q™&€GÐ;ØÔ!ˆÜ× Ò ¤¨Q¯U©UÓ!3°YÓ?ˆÜ×(Ò(¨Ö.ˆDØ�G‰G�N‰N˜4Ö ñ /ä! !§%¡%¨Ó3ˆßˆ4Ø�G‰G�N‰N˜4Ö ñ "ð ‡N�NÑ.€NÑ/Ü×Ò˜	Ñ,¨wÑ6Ô7Ü�	 WÔ8Ü� g°iÔ@Ü	ÐCÄCÈ	ÃNÐCSð TÜ�h“-� ¬C°«L¨>¸ð<õ =ùò! 3ùâ>ùÚ8ùÚ;s*   Á G?Á>HÂHÂ!H	Â8H	ÃHÃHc                 ó    • U R                   S;   $ )aå  Motorway's CSV enforces the 210 mile limit but gives a location not
exact miles, so distance is trusted to the filter. Auction4Cars and
Dealer Auction give only a location name, no mile figure at all, so the
mile based check is skipped for both (Mark's decision 2026-08-19 and
2026-08-20), each has its own location allow list in settings instead
(depot name for Auction4Cars, county for Dealer Auction, see
pricing.gate_failures). Carwow carries real distance and is checked
normally.)rP   rR   rS   ©rc   r[   s    r0   Ú_distance_ok_forr  Ì  s   € ð �:‰:ÐGÑGÐGrE   c                 ó    • U R                   S;   $ )zúAuction4Cars and DealerWay have no 1 to 5 condition grade at all
(Mark's decision 2026-08-19, and 2026-08-24 for DealerWay, which only
has pass/fail Test Driven, Bodywork and Mechanical status flags),
condition is judged by eye from the card instead.)rR   rT   r  r[   s    r0   Ú_grade_ok_forr  Ø  s   € ð
 �:‰:Ð6Ñ6Ð6rE   c                  óF   • [         c  [        R                  " 5       q [         $ )ad  The buying rule overrides saved from the settings page, read once per
process and cached (a fresh daily_run.py invocation always re-reads, and
settings are not expected to change mid run). Every caller that needs a
rule threshold or ban list goes through this, exactly so a new one cannot
be forgotten at some call site the way assume_distance_ok once was.)Ú_settings_cacher   Úget_settingsrG   rE   r0   r€  r€  ã  s   € ô ÑÜŸ/š/Ó+ˆÜÐrE   c                  óV   • 0 [         E[        5       R                  S5      =(       d    0 E$ )zèEvery automation's own enabled/disabled state, INTEGRATION_DEFAULTS
overridden by whatever the settings page's Integrations tab has saved.
A shallow merge is enough since each entry is just {"enabled": bool},
nothing nested to lose.rÓ  )ÚINTEGRATION_DEFAULTSr€  r+   rG   rE   r0   rà  rà  ï  s(   € ð
 SÔ"ÐR¤}£×':Ñ':¸>Ó'J×'PÈbÐRÐRrE   c           	      ó�   • [        [        5       R                  U [        R                  U 0 5      5      R                  SS5      5      $ )Nr!   T)rM   rà  r+   r  rÍ   s    r0   rc  rc  ÷  s9   € Ü”“×#Ñ# CÔ)=×)AÑ)AÀ#ÀrÓ)JÓK×OÑOÐPYÐ[_Ó`ÓaÐarE   c                  óV   • 0 [         E[        5       R                  S5      =(       d    0 E$ )zïEvery platform's own enabled/disabled state, PLATFORM_ENABLED_DEFAULTS
overridden by whatever each platform's own settings tab has saved. Flat
{key: bool}, not nested like _integrations, there is nothing else to a
platform's enabled state.rÖ  )ÚPLATFORM_ENABLED_DEFAULTSr€  r+   rG   rE   r0   rá  rá  û  s*   € ð
 \Ô'Ð[¬M«O×,?Ñ,?Ð@RÓ,S×,YÐWYÐ[Ð[rE   c           	      óp   • [        [        5       R                  U [        R                  U S5      5      5      $ )NT)rM   rá  r+   r  rÍ   s    r0   r‚  r‚    s-   € ÜÔ"Ó$×(Ñ(¨Ô.G×.KÑ.KÈCÐQUÓ.VÓWÓXÐXrE   c                 ó*  • [        5       n[        U 5      (       a  g[        X5      (       a  g[        X[	        U 5      [        U 5      US9nU Vs/ s H1  nSUR                  5       ;  d  M  SUR                  5       ;  d  M/  UPM3     nnU(       + $ s  snf )aw  The gate checks we can make from the list alone, used to decide which
cars are worth opening the detail page and valuing. Owners and exact engine
are confirmed later, on the car's page. Reserve too, for Carwow specifically
(2026-08-19): Carwow removed Reserve price from the list card, CAP clean
stands in for it but only reads from the car's own page, so a car cannot
be rejected here for merely not having a reserve YET, only for one that is
known and too high. A platform that already knows its reserve at list
time (Motorway, Auction4Cars' starting price) still rejects immediately as
before, this only defers the missing case.TF)Úassume_distance_okÚassume_grade_okÚsettingszprevious ownerszreserve price is missing)r€  Ú_release_forr   r    r  r  r  )r\   rI   r  Úfailsr}   s        r0   Ú_list_gate_okr    sŽ   € ô ‹€HÜ�C×Ñð Ü�3×!Ñ!ØÜ˜#Ô9IÈ#Ó9NÜ*7¸Ó*<ÀxñQ€Eáó >š�1Ð!2¸!¿'¹'»)Ñ!C‹QØ+°1·7±7³9Ñ<÷ ™€Eð >àŒ9Ðùò>s   ÁBÁ+BÂBc                 óà   • U  Hc  nUR                   S:X  d  M  [        UR                  5      S:”  d  M0  UR                   Vs/ s H  nSU;  d  M  UPM     nnU(       d  M]  X1l        Me     gs  snf )až  Drop a "so and so is missing" reason from a rejection that also has
a real one (Steven 2026-09-02, Step 2 of the review). A Carwow or
DealerWay car only ever has its owners and reserve read on its own
page, and that page is only visited for cars that passed the cheap
list gate; so a car rejected for being 11 years old and grade 4 also
read "owners missing, reserve missing", about 200 car-days a week,
which looked like a reader gap until a live check showed the facts
were on the page all along. The real reason stays; a car rejected for
NOTHING BUT a missing fact keeps that reason, since it genuinely is
the unknown that dropped it. Never touches status or pricing.r¼   r9   z
is missingN)rk   rš   râ   )rÞ   ro   ÚrÚreals       r0   rÕ   rÕ     sU   € ó ˆØ�8‰8�zÕ!¤c¨!¯)©)£n°qÕ&8Ø ŸyšyÓBšy˜!¨LÀÑ,A—A™yˆDÐBßˆtØ –	ò	 ùâBs   Á
A+ÁA+r'  )ÚatÚmapc                  óÚ   • SSK n U R                  5       nU[        S   -
  S:”  a&   [        R                  " 5       [        S'   U[        S'   [        S   $ ! [         a    0 [        S'    N'f = f)z·reg to release row for the cars Steven released from a hold, read
from the database at most once every 30 seconds (a run assesses over a
thousand cars, one query each would be silly).r   Nr"  r7   r#  )r=   Ú_released_cacher   Úreleased_mapr¦   )r=   rA   s     r0   Ú	_releasedr'  4  sl   € ó Ø
�)‰)‹+€CØ
Œ_˜TÑ"Ñ" RÓ'ð	(Ü%'§_¢_Ó%6ŒO˜EÑ"ð !$Œ˜ÑÜ˜5Ñ!Ð!øô ó 	(Ø%'ŒO˜EÓ"ð	(ús   ¦A ÁA*Á)A*c           	      ór   • [        5       R                  [        R                  " [	        U SS5      5      5      $ )NrW   rX   )r'  r+   r   r   rZ   r[   s    r0   r  r  C  s%   € Ü‹;�?‰?œ2Ÿ<š<¬°°U¸BÓ(?Ó@ÓAÐArE   c           
      ób   • [        X[        U 5      [        U 5      [        5       [	        U 5      S9$ )z‰Assess a car, with the per source escape hatches decided above, and
the release row when Steven has released it from a hold (2026-09-10).)r  r  r  Úrelease)r   r  r  r€  r  )r\   rI   s     r0   rÔ   rÔ   G  s/   € ô �#Ô1AÀ#Ó1FÜ"/°Ó"4¼}»Ü& sÓ+ñ-ð -rE   Úlogszmotorway_csv_failed.txtc                  óÄ   •  [        [        SS9 n U R                  5       R                  5       =(       d    S sS S S 5        $ ! , (       d  f       g = f! [         a     g f = f)Nru   rv   )ry   Ú
CSV_FAILEDÚreadr  rï   )r}   s    r0   Ú_csv_failed_onr/  [  sE   € ðÜ”* wÒ/°1Ø—6‘6“8—>‘>Ó#×+ t÷ 0×/×/ûäó Ùðús-   ‚A �'A·	A Á
AÁA ÁA Á
AÁAc                  ó®   •  [         R                  R                  [        5      (       a  [         R                  " [        5        g g! [
         a     gf = f)z‡Remove the v3.14.48 marker from its old, git visible place if it is
still there, so the tree is clean for the next update. Best effort.TF)rì   rí   ÚexistsÚCSV_FAILED_OLDÚremoverï   rG   rE   r0   Úclear_old_csv_markerr4  c  sJ   € ðÜ�7‰7�>‰>œ.×)Ñ)Ü�IŠI”nÔ%Øð *ð
 øô ó ØØðús   ‚AA Á
AÁAc                 ó¦  • [        5          [        R                  " [        R                  R	                  [
        5      SS9  U cC  [        R                  R                  [
        5      (       a  [        R                  " [
        5        g [        [
        SSS9 nUR                  U 5        S S S 5        g ! , (       d  f       g = f! [         a     g f = f)NT©Úexist_okrt   ru   rv   )r4  rì   Úmakedirsrí   Údirnamer-  r1  r3  ry   rÂ   rï   )rC   r}   s     r0   Ú_note_csv_failedr:  o  s…   € ÜÔð	Ü
�Š”B—G‘G—O‘O¤JÓ/¸$Ò?Ø‰;Ü�w‰w�~‰~œj×)Ñ)Ü—	’	œ*Ô%ØÜ”*˜c¨GÒ4¸Ø�G‰G�CŒL÷ 5×4Ö4ûäó Ùðús6   ŒA;C ÂC ÂB2Â)C Â2
C Â<C Ã C Ã
CÃCc                 ó.   • [        U 5      =(       a    X:H  $ )z‰Whether to go straight to the cards: only when the CSV export already
failed today. Yesterday's failure earns one more try. Pure, tested.rL   )Ú	failed_onrI   s     r0   Ú	_skip_csvr=  }  s   € ô �	‹?×1˜yÑ1Ð1rE   c                 óÔ  • [         R                  R                  5       R                  5       nS n[	        [        5       U5      (       a  Sn[        S5        O" [        R                  " U 5      n[        S 5        UbZ   [#        X5      n[        R                  " SSSU S35        [        R$                  " USU5        [        S['        U5       S35        U$ [        R                  " SS5        [        R$                  " WSU5        [        S['        U5       S35        U$ ! [         ax  n[        R                  " US5      nSU;   d6  SUR                  5       ;   d"  [        R                  " SS[!        U5      5        e [        U5        [        SU S	35         S nAGN#S nAff = f! [         a:  n[        R                  " SSS
U S[        R                  " US5       S35        e S nAff = f)Nz4failed earlier today, not tried again until tomorrowz[Motorway: the CSV export failed earlier today. Reading the cards on the page straight away.éÈ   ÚCSVÚdownloadr   Fz!Motorway: the CSV export failed (zA). Reading the cards on the page and each car's own page instead.zCSV export failed (z!); reading the cards failed too (Ú)TzVRead from the cards on the page: Motorway's CSV export is failing on Motorway's side (ra   rP   zMotorway: read zH cars from the cards on the page (CSV export failing on Motorway's side)z" cars from the filtered CSV export)r>   r<   rI   r@   r=  r/  r¨   r   Úread_exportr:  r¦   r   Ú
first_liner  r   Úrecord_site_healthÚstrÚ_read_motorway_cardsÚrecord_sightingsrš   )r©   rN   Ú	today_isoÚwhyrû  ÚeÚe2s          r0   rÏ   rÏ   ƒ  sð  € ô —‘×#Ñ#Ó%×/Ñ/Ó1€IØ
€CÜ”Ó! 9×-Ñ-ØDˆÜÐkÕlð		~Ü×'Ò'¨Ó*ˆDÜ˜TÔ"ð �ð	Ü'¨Ó5ˆDô 	×Ò˜j¨$ð  3Ið  JMð  INð  NPð  1Qô  	RÜ
×Ò˜D *¨iÔ8Ü�¤ D£	˜{Ð*rÐsÔtØˆÜ×Ò˜* dÔ+Ü×Ò˜˜j¨)Ô4Ü	ˆOœC ›I˜;Ð&HÐ
IÔJØ€Køô7 ó 	~Ü—/’/ ! SÓ)ˆCØ˜S“L J°#·)±)³+Ó$=Ü×%Ò% j°%¼¸Q»Ô@ØÜ˜YÔ'ÜÐ5°c°UÐ:{Ð|×}Ò}ûð	~ûô" ó 	Ü×!Ò! *¨eÐ7JÈ3È%ÐOpÔqu÷  rAò  rAð  BDð  FIó  rJð  qKð  KLð  6Mô  NØûð	ús1   Á!D Á<F# Ä
F Ä(A-FÆF Æ#
G'Æ-5G"Ç"G'c                 ó  ^• [         R                  R                  U5      n[        R                  " U SSS9n UR
                  (       a  UR
                  S   OUR                  5       mTR                  [        R                  S   S   SSS9   TR                  S	S
S9  TR                  S5        Sn[        S5       HJ  nTR                  R                  SS5        TR                  S5        TR!                  S5      nXd::  a    OUnML     ["        R$                  " TR'                  5       5      nU(       d  [        S5      e[)        S[+        U5       S35        Sn[-        U5       Hb  u  pš[/        SSU	S-    S[+        U5       SU
R0                  =(       d    U
R2                   35         [5        SXª4U4S jjSS9(       a  US-  nMb  Md     U(       a  [)        SU S[+        U5       S 35        UR;                  5         U$ ! [         a    [        S5      ef = f! [         aO  n[)        SU
R0                  =(       d    U
R2                   S[6        R8                  " US5       35         SnAGM  SnAff = f! UR;                  5         f = f)!zýThe Motorway list read the older way: the cards on the stock page,
then each car's own page for owners, engine size, fuel, service
history and CAP (motorway.enrich_from_detail, with the same detail
cache the other platforms use). One continuous session.r   Tr†   r   Ú	stock_urlrˆ   éÈ¯  rŠ   úa[href^="/vehicles/"]é0u  ©rŒ   zmMotorway stock list did not show any vehicle cards. The page may have changed, or the login may have expired.éÜ  é(   i   i„  z?() => document.querySelectorAll('a[href^="/vehicles/"]').lengthz)Read the Motorway page but found no cars.z
Motorway: z/ cards on the page, reading each car's own pagerî  zMotorway car r9   r”   ú: c                 ó2   >• [         R                  " TU 5      $ rŸ  )r   Úenrich_from_detail©rÝ   r°   s    €r0   rË   Ú&_read_motorway_cards.<locals>.<lambda>Û  s   ø€ Ä×A\ÒA\Ð]aÐcdÔAerE   )r_   rW   )Úrequirez  could not read éx   Nú detail reads reused from cache)r>   r<   r·   r   r�   rž   rŸ   r¡   ÚSITESr¢   r¦   ÚRuntimeErrorÚwait_for_timeoutÚrangeÚmouseÚwheelÚevaluater   Úparse_listingÚcontentr¨   rš   r    r™   rW   r˜   Ú_enrich_with_cacher   rD  r§   )r©   rN   rI   r¯   Úseenrj   r  rû  Úreusedr±   rÝ   rK  r°   s               @r0   rG  rG  ²  sK  ø€ ô
 �M‰M×'Ñ'¨	Ó2€EÜ
×
%Ò
% a¨¸dÑ
C€Cð*Ø"ŸyŸyˆs�y‰y˜Š|¨c¯l©l«nˆØ�	‰	”'—-‘- 
Ñ+¨KÑ8ÐEWÐafˆ	Ñgð	AØ×"Ñ"Ð#:ÀEÐ"ÑJð 	×Ñ˜dÔ#ð ˆÜ�r–ˆAØ�J‰J×Ñ˜Q Ô%Ø×!Ñ! #Ô&Ø—‘ÐaÓbˆAØ‹yÙØŠDñ ô ×%Ò% d§l¡l£nÓ5ˆÞÜÐJÓKÐKÜ�
œ3˜t›9˜+Ð%TÐUÔVØˆÜ˜d–O‰DˆAÜ�i =°°Q±°°t¼CÀ»I¸;ÀbÈÏÉ×I_ÐRS×R_ÑR_ÐH`Ð!aÔbð_ô & j°!Ö5eØ.@÷Bà˜a‘K’FñBñ $ö Ü�J˜v˜h d¬3¨t«9¨+Ð5TÐUÔVà�	‰	ŒØ€KøôM ó 	AÜð  @ó Að Að	Aûô@ ó _ÜÐ)¨!¯%©%×*@°1·=±=Ð)AÀÄDÇOÂOÐTUÐWZÓD[ÐC\Ð]×^Ó^ûð_ûð
 	�	‰	�úsP   ¸AI4 ÂG? ÂD
I4 Æ)HÇ(I4 Ç?HÈI4 È
I1È"AI,É%I4 É,I1É1I4 É4Jc                 ó–  ^^• U=(       d    [         R                  nTR                  (       a   [         R                  " TR                  5      OTR                  nU(       aY  [         R
                  " XPUS9mTbA  [        UU4S jU 5       5      (       a&  TR                  5        H  u  pg[        TXg5        M     g[        T5      nU" 5         U(       ar  [        T5      n	U	R                  5        VVs0 s H5  u  pgUR                  S5      (       a  M  XxR                  U5      :w  d  M3  Xg_M7     n
nn[         R                  " XPX£S9  gs  snnf )aD  Run a platform's own detail page read (enrich_fn, a zero argument
closure that already knows which page and car to use, typically
lambda: some_module.enrich_from_detail(page, c)), but skip the real page
visit when a cached read for this exact car is still fresh (Mark
2026-08-24: "are these cars info... being added to a database... rather
than a full scrape", the slow part of a refresh on a multi day auction
where the same car legitimately reappears run after run). Whatever the
real read changes (owners, service history, disclosed issues, photos,
and on Carwow/Auction4Cars the reserve itself, which only that page ever
discloses) is a genuine before/after diff, not a fixed field list, so
this needs no update when a reader gains a new field. Reserve rides
along with the same short TTL as everything else (Mark's own choice),
never a separate longer one, so a cached figure is never more than a
few hours stale. Keyed on the normalised reg wherever the list card
already carries one (Carwow, DealerWay); Auction4Cars' own reg is only
ever read FROM its detail page, so listing_url stands in as the key
there instead, a candidate is always the same real auction lot on that
stable URL day to day, until the real reg lands among the cached fields
on the first successful read. Returns True on a cache hit (nothing was
actually read this call). db_path is resolved to db.DB_PATH at call time
if not given, never baked in as a default at import time (db.py's own
functions default their path at definition time, so a test wanting an
isolated database must pass one explicitly here too, see test_pricing.py).
require names fields the car must end up with for a cached read to
count as a hit: either the row carries the field or the car already
has it from its card. A row without one the card also lacks (saved
before the reader learnt to read it) is a miss.©rí   c              3   ón   >#   • U  H*  nTR                  U5      =(       d    [        TUS 5      v •  M,     g 7frŸ  )r+   rZ   )rW  r}   rÝ   Úcacheds     €€r0   rX  Ú%_enrich_with_cache.<locals>.<genexpr>  s,   øé € Ð%\ÒT[Èq f§j¡j°£m×&J´w¸qÀ!ÀTÓ7JÔ&JÒT[ùs   ƒ25Trj   F)r   ÚDB_PATHrW   r   r˜   Úget_cached_detailÚallrm   Úsetattrr   rn   r+   Úsave_cached_detail)ÚplatformrÝ   Ú	enrich_fnÚdb_pathrZ  r/   rp   rq   ÚbeforeÚafterÚchangedrl  s    `         @r0   rf  rf  ç  sí   ù€ ð8 ×#œŸ™€GØ!"§§Œ"�,Š,�q—u‘uÔ
¨A¯M©M€CÞ
Ü×%Ò% c¸'ÑBˆØÑ¤#Õ%\ÑT[Ó%\×"\Ñ"\ØŸ™ž‘�Ü˜˜1Ö ñ 'àÜ�A‹Y€FÙ„KÞ
Ü�q“	ˆØ$)§K¡K¤MÔb¢M™D˜A¸¿¹Àc×9J“4Èq×T^ÑT^Ð_`ÓTaÑOa“4�1’4¡MˆÑbÜ
×Ò˜c¨WÒCØùó cs   Ã0EÄEÄ&Ec                 óN  ^
• Sn[         R                  " U SSS9n UR                  (       a  UR                  S   OUR                  5       m
[        R
                  " T
S S9n[        R                  " SS5        [        R                  " USU5        [        S	[        U5       S
35        [        R                  R                  U5      nU Vs/ s H  n[        XT5      (       d  M  UPM     nn[        S[        U5       S35        Sn[        U5       Hc  u  p…[!        SSUS-    S[        U5       SUR"                  =(       d    UR$                   35         ['        SXU4U
4S jj5      (       a  US-  nMc  Me     U(       a  [        SU S[        U5       S35        [        R                  " U Vs/ s H+  oUR"                  (       a  M  [+        USS5      (       d  M)  UPM-     snSU5        UR/                  5         U$ s  snf ! [(         a:  n	[        SUR"                  =(       d    UR$                   SU	 35         Sn	A	GM.  Sn	A	ff = fs  snf ! [(         a:  n	[        SU	 S35        [        R                  " SS[-        U	5      5         Sn	A	N¢Sn	A	ff = f! UR/                  5         f = f)a  Returns the cars read, or None if the list itself could not be read at
all (a dead login, a changed page), distinct from a genuinely empty list
(nothing currently matches the saved filter). run() falls back to the
last known good cars for this platform on None rather than silently
wiping it to zero (caught live 2026-08-21: a partial run refreshing just
one platform, that platform's own read failing, wiped it from the
shortlist entirely with only a console line as evidence, exactly what
golden rule 5 exists to prevent).Nr   Tr†   r   c                 ó(   • [        SSU  SU S35      $ )Nrî  zCarwow page rE  ú cars so far©r™   ©r  r	  s     r0   rË   Ú_read_carwow.<locals>.<lambda>'  s   € ¬9°YÀ,ÈqÈcÐQSÐTYÐSZÐZfÐ@gÔ+hrE   )Úon_pagerQ   zCarwow: read ú auction carszCarwow: z; pass the list level checks, reading their pages for ownersrî  zCarwow detail r9   r”   rU  c                 ó2   >• [         R                  " TU 5      $ rŸ  )r   rW  rX  s    €r0   rË   r~  3  s   ø€ ¼v×?XÒ?XÐY]Ð_`Ô?arE   ú  could not read detail for r\  rY   zCarwow: could not read (z3). Falling back to the last known good Carwow cars.F)r   r�   rž   rŸ   r   Úread_all_pagesr   rE  rH  r¨   rš   r>   r<   r·   r  r    r™   rW   Ú_listing_idrf  r¦   rZ   rF  r§   )r©   rN   rû  r¯   rI   rÝ   Ú
candidatesrh  r±   rK  r°   s             @r0   rÐ   rÐ     sA  ø€ ð €DÜ
×
%Ò
% a¨¸DÑ
A€Cð"Ø"ŸyŸyˆs�y‰y˜Š|¨c¯l©l«nˆô ×$Ò$ØÑhñjˆä
×Ò˜h¨Ô-Ü
×Ò˜D (¨IÔ6Ü�œc $›i˜[¨Ð6Ô7ä—‘×+Ñ+¨IÓ6ˆÙ!%ÓA¢˜A¬°q×)@—a¡ˆ
ÐAÜ�œ˜Z›Ð)Ð)dÐeÔfØˆÜ˜jÖ)‰DˆAÜ�i >°!°a±%°¸¼SÀ»_Ð<MÈRÐPQ×PUÑPU×PfÐYZ×YfÑYfÐOgÐ!hÔiðTÜ% h°Ö3a×bÑbØ˜a‘K’Fñ cñ *ö Ü�H˜V˜H D¬¨Z«Ð(9Ð9XÐYÔZô 	×Ò©
Ódª
 1¿%½%›QÄGÈAÈ}Ð^b×DcŸQ©
ÑdØ$ iô	1ð 	�	‰	ŒØ€Kùò+ Bøô ó TÜÐ4°Q·U±U×5K¸a¿m¹mÐ4LÈBÈqÈcÐR×SÓSûðTüò eøäó 7ÜÐ(¨¨Ð+^Ð_Ô`Ü
×Ò˜h¨¬s°1«v×6Ñ6ûð7ûð 	�	‰	�ús…   ›B0I ÃG:Ã#G:Ã)A*I ÅG?Å1<I Æ-IÇIÇIÇ	I Ç:I Ç?
IÈ	.H>È7I È>IÉI É
JÉ0J
ÊJ Ê
JÊJ ÊJ$c           
      óÌ  ^
• Sn[         R                  R                  U5      n[        R                  " U SSS9n UR
                  (       a  UR
                  S   OUR                  5       m
T
R                  [        R                  S   S   SSS	9  T
R                  S
SS9  T
R                  S5        [        R                  " T
US S9n[        R                  " SS5        [        R                  " USU5        [!        S[#        U5       S35        U Vs/ s H  n[%        XS5      (       d  M  UPM     nn[!        S[#        U5       S35        Sn['        U5       HC  u  p…[)        SSUS-    S[#        U5       35         [+        SXU4U
4S jj5      (       a  US-  nMC  ME     U(       a  [!        SU S[#        U5       S35        UR3                  5         U$ s  snf ! [,         a&  n	[!        SUR.                   SU	 35         Sn	A	M«  Sn	A	ff = f! [,         a:  n	[!        SU	 S35        [        R                  " SS[1        U	5      5         Sn	A	N‰Sn	A	ff = f! UR3                  5         f = f) zÉReturns None (not an empty list) if the list itself could not be read,
so run() can fall back to the last known good cars for this platform
rather than silently wiping it, see _read_carwow's docstring.Nr   Tr†   r   rN  rˆ   rO  rŠ   zdiv.vehicle-card-containerrQ  rR  rS  c                 ó(   • [        SSU  SU S35      $ )Nrî  zAuction4Cars page rE  r{  r|  r}  s     r0   rË   Ú$_read_auction4cars.<locals>.<lambda>Y  s   € ¤Y¨yÐ<NÈqÈcÐQSÐTYÐSZÐZfÐ:gÔ%hrE   )rI   r  rR   zAuction4Cars: read ú& auction cars ending within the windowzAuction4Cars: zP pass the list level checks, reading their pages for reg, owners and guide valuerî  zAuction4Cars detail r9   r”   c                 ó2   >• [         R                  " TU 5      $ rŸ  )r   rW  rX  s    €r0   rË   rˆ  e  s   ø€ Ä\×EdÒEdÐeiÐklÔEmrE   r‚  rU  r\  zAuction4Cars: could not read (z9). Falling back to the last known good Auction4Cars cars.F)r>   r<   r·   r   r�   rž   rŸ   r¡   r]  r¢   r_  r   rƒ  r   rE  rH  r¨   rš   r  r    r™   rf  r¦   r˜   rF  r§   ©r©   rN   rû  rI   r¯   rÝ   r…  rh  r±   rK  r°   s             @r0   rÑ   rÑ   E  s5  ø€ ð €DÜ�M‰M×'Ñ'¨	Ó2€EÜ
×
%Ò
% a¨À$Ñ
G€Cð#Ø"ŸyŸyˆs�y‰y˜Š|¨c¯l©l«nˆØ�	‰	”'—-‘- Ñ/°Ñ<ÐI[Ðejˆ	ÑkØ×ÑÐ;ÀUÐÑKØ×Ñ˜dÔ#ô ×*Ò*Ø˜Ùhñjˆô 	×Ò˜n¨dÔ3Ü
×Ò˜D .°)Ô<ÜÐ#¤C¨£I ;Ð.TÐUÔVá!%ÓA¢˜A¬°q×)@—a¡ˆ
ÐAÜ�œs :›Ð/ð 0Dð Eô 	FàˆÜ˜jÖ)‰DˆAÜ�iÐ#7¸¸A¹°w¸dÄ3ÀzÃ?ÐBSÐ!TÔUðKÜ% n°aÖ9m×nÑnØ˜a‘K’Fñ oñ *ö Ü�N 6 (¨$¬s°:«Ð.?Ð?^Ð_Ô`ð
 	�	‰	ŒØ€Kùò% Bøô ó KÜÐ4°Q·]±]°OÀ2ÀaÀSÐI×JÒJûðKûô ó =ÜÐ.¨q¨cÐ1jÐkÔlÜ
×Ò˜n¨e´S¸³V×<Ñ<ûð=ûð 	�	‰	�úsm   ºCH
 ÄGÄ*GÄ0A
H
 Å;GÆ(H
 ÇH
 Ç
HÇ!HÇ<H
 ÈHÈH
 È

IÈ0I	ÉI É	IÉI ÉI#c                 óT  • Sn [         R                  " U SS9n[        R                  " SS5        [        R                  " USU5        [        S[        U5       S35        U$ ! [         a;  n[        SU S	35        [        R                  " SS
[        U5      5         SnAU$ SnAff = f)a½  Unlike Auction4Cars, everything the gate needs (registration, real
NAMA grade, owners, VAT status, reserve stand in) is already on the list
card (Mark 2026-08-20), so there is no detail page enrichment pass here
at all, just a read and a gate. Returns None (not an empty list) if the
list itself could not be read, so run() can fall back to the last known
good cars for this platform rather than silently wiping it, see
_read_carwow's docstring.NTr†   r   rS   zDealer Auction: read r€  z Dealer Auction: could not read (z;). Falling back to the last known good Dealer Auction cars.F)	r   Ú	read_liver   rE  rH  r¨   rš   r¦   rF  )r©   rN   rû  rK  s       r0   rÒ   rÒ   s  s£   € ð €Dð>Ü×&Ò& q°4Ñ8ˆÜ
×Ò˜o¨tÔ4Ü
×Ò˜DÐ"2°IÔ>ÜÐ%¤c¨$£i [°Ð>Ô?ð €Køô ó >ÜÐ0°°Ð3nÐoÔpÜ
×Ò˜o¨u´c¸!³f×=Ð=Ø€Kûð>ús   „AA" Á"
B'Á,0B"Â"B'c           
      óÞ  ^
• Sn[         R                  R                  U5      n[        R                  " U SSS9n UR
                  (       a  UR
                  S   OUR                  5       m
T
R                  [        R                  SSS9  T
R                  S	5        T
R                  S
5      (       d  [        S5      e[        R                  " T
US9n[        R                  " SS5        [        R                   " USU5        [#        S[%        U5       S35        U Vs/ s H  n['        XS5      (       d  M  UPM     nn[#        S[%        U5       S35        Sn[)        U5       HC  u  p…[+        SSUS-    S[%        U5       35         [-        SXU4U
4S jj5      (       a  US-  nMC  ME     U(       a  [#        SU S[%        U5       S35        UR5                  5         U$ s  snf ! [.         a&  n	[#        SUR0                   SU	 35         Sn	A	M«  Sn	A	ff = f! [.         a:  n	[#        SU	 S35        [        R                  " SS[3        U	5      5         Sn	A	N‰Sn	A	ff = f! UR5                  5         f = f)ar  DealerWay's list card gives registration, distance and its own real
or issue flag statuses (Mark 2026-08-24), but not owners or the real
disclosed issue text, so gate passing candidates still get a detail
page visit for those, the same shape as Carwow and Auction4Cars.
Returns None (not an empty list) if the list itself could not be read,
see _read_carwow's docstring.Nr   Tr†   r   ÚnetworkidlerO  rŠ   rS  za[href^="/listings/"]z/DealerWay listings did not render any vehicles.)rI   rT   zDealerWay: read r‰  zDealerWay: zL pass the list level checks, reading their pages for owners and issue detailrî  zDealerWay detail r9   r”   c                 ó2   >• [         R                  " TU 5      $ rŸ  )r   rW  rX  s    €r0   rË   Ú!_read_dealerway.<locals>.<lambda>£  s   ø€ Ä)×B^ÒB^Ð_cÐefÔBgrE   r‚  rU  r\  zDealerWay: could not read (z6). Falling back to the last known good DealerWay cars.F)r>   r<   r·   r   r�   rž   rŸ   r¡   r   ÚLIST_URLr_  Úquery_selectorr^  rƒ  r   rE  rH  r¨   rš   r  r    r™   rf  r¦   r˜   rF  r§   r‹  s             @r0   rÓ   rÓ   ‡  s*  ø€ ð €DÜ�M‰M×'Ñ'¨	Ó2€EÜ
×
%Ò
% a¨¸tÑ
D€CðØ"ŸyŸyˆs�y‰y˜Š|¨c¯l©l«nˆØ�	‰	”)×$Ñ$°Èˆ	ÑNØ×Ñ˜dÔ#Ø×"Ñ"Ð#:×;Ñ;ÜÐPÓQÐQÜ×'Ò'¨°EÑ:ˆÜ
×Ò˜k¨4Ô0Ü
×Ò˜D +¨yÔ9ÜÐ ¤ T£ Ð+QÐRÔSá!%ÓA¢˜A¬°q×)@—a¡ˆ
ÐAÜ�œC 
›OÐ,ð -@ð Aô 	BàˆÜ˜jÖ)‰DˆAÜ�iÐ#4°Q¸±U°G¸4ÄÀJÃÐ?PÐ!QÔRðKÜ% k°1Ö6g×hÑhØ˜a‘K’Fñ iñ *ö Ü�K ˜x t¬C°
«OÐ+<Ð<[Ð\Ô]ð
 	�	‰	ŒØ€Kùò% Bøô ó KÜÐ4°Q·]±]°OÀ2ÀaÀSÐI×JÒJûðKûô ó :ÜÐ+¨A¨3Ð.dÐeÔfÜ
×Ò˜k¨5´#°a³&×9Ñ9ûð:ûð 	�	‰	�úsm   ºC!H ÄGÄ3GÄ9A
H ÆG Æ!(H ÇH Ç 
HÇ*HÈH ÈHÈH È
IÈ0IÉI ÉIÉI ÉI,c                 óÆ  ^G^H^I^J^K^L^M^N^O^P^Q^R^S^T^U^V• [         R                  " 5         [         R                  " 5       nU(       a  [        SU S35        [         R                  " 5       nU(       a  [        SU S35         [        SS9nU(       a(  UR                  S5      (       a  [        SUS    S35        U(       a  [        U5      O	[        5       nU[        [        5      -
  n	U	(       a  [        S[        U	5       S[         35      e[        5       n
[        X:5      mV/ / smUmQ[         Vs1 s H  oºR                  US5      (       d  M  UiM     nn[        TVU
5      n [        5       u  pïnn/ / / nnnU(       a4  Uc1  [!        U[#        [$        R$                  R'                  5       5      5      nU=(       d'    [#        [$        R$                  R'                  5       5      nU(       d  U(       d  U(       a(  [)        UU5      (       d  [        SU SU S35        / / / npþU(       aÿ  U Vs1 s H  nUTV;  d  M  [*        U   iM     nnU Vs/ s H!  nUR,                  R.                  U;   d  M  UPM#     nnU Vs/ s H!  nUR,                  R.                  U;   d  M  UPM#     nnU Vs/ s H!  nUR,                  R.                  U;   d  M  UPM#     nnSR1                  UV4S j[         5       5      n[3        U5      [3        U5      -   [3        U5      -   n[        SU SU S35        [$        R4                  R7                  U5      n[        SU S35        [9        5        mT[;        S5      (       a˜  [=        SS5         [>        R@                  " TTSS9u  nnn[         RB                  " UU5        [         RD                  " U5        [        S[G        URI                  5       5       S[3        U5       S[G        S  U 5       5       S!35        U(       + mRSmNS$mGS%mSS%mOSmPS%mLS%mMS& mIUGURUSUT4S' jmKUGUIUNUOUPUT4S( jmJULUM4S) jmHUHUJUKUNUR4S* jn[         RJ                  " 5       n[L        [N        [P        [R        [T        S+.n / / / n#n"n![         Vs/ s H  nUTV;   d  M  UPM     sn GH\  n[*        U   n$[=        S,S-U$ S.35        US/:X  a  U U   " TTU5      n%GO%U U   " TTU5      n%U%Gc  U Vs/ s H!  nUR,                  R.                  U$:X  d  M  UPM#     n&nU Vs/ s H!  nUR,                  R.                  U$:X  d  M  UPM#     n'nU Vs/ s H!  nUR,                  R.                  U$:X  d  M  UPM#     n(nU!U&-  n!U"U'-  n"U#U(-  n#[3        U&5      [3        U'5      -   [3        U(5      -   n)[        U$ S0U) S135        [         RV                  " USS2U) S335        TQRY                  U5        [[        UU!-   UU"-   UU#-   UU5        GMV  U%=(       d    / n%[3        U%5      n*U% V+s/ s H+  n+[         R\                  " U+R^                  5      U;  d  M)  U+PM-     n%n+U*[3        U%5      -
  (       a  [        S4U*[3        U%5      -
   S5U$ S635        US/:X  Ga]  U% V+s/ s H+  n+[a        U+U5      (       d  M  U+Rb                  (       a  M)  U+PM-     n,n+U,(       Ga  [=        S,S-[3        U,5       S73S%[3        U,5      5        [d        Rf                  " TTS/SS89n- U-Rh                  (       a  U-Rh                  S%   OU-Rk                  5       n.[m        U,S95       HD  u  n/n+ [n        Rp                  " U.U+5        [=        S,S:U/ S;[3        U,5       3U/[3        U,5      5        MF     U-Rs                  5         [G        S< U, 5       5      n0[3        U,5      S=:¼  a-  U0S>[3        U,5      -  :  a  [u        S?U0 S;[3        U,5       S@35      eU% V+s/ s H  n+[w        U+U5      PM     n1n+[y        U15        U1 Vs/ s H  nURz                  SA;   d  M  UPM     n2nU2R}                  SB SC9  U (       a  U2SU  OU2n3[        SU$ SD[3        U25       SE3U (       a  SF[3        U35       SG3OSH-   5        [        U15      =(       a    U2(       + n4U4(       añ  S%SIK@JAn5  U5" 5       n6U1 HM  n[…        USJS5      =(       d    […        USKS5      =(       d    /  H  n7U6[‡        U75      SSL ==   S9-  ss'   M     MO     U6R‰                  SM5       H#  u  n7n8[        SNU8 S;[3        U15       SDU7 35        M%     U6(       a  U6R‰                  S95      S%   OSOn9[         RV                  " USSP[3        U15       SQU9S9    S;[3        U15       SDU9S%    35        [=        SRSS[3        U35       S5U$ ST3S%[3        U35      5        [m        U35       Hè  u  n/nUR,                  n+TR(       a  TN(       a  [        SU5          O¿U" U+5        [        SNU+R^                   S5U+RŠ                   S5U+RŒ                   SV[�        U+R�                  5       SW[�        U+R’                  5       3
5        [=        SRU$ SXU/S9-    S;[3        U35       SDU+R^                   S5U+RŠ                   S5U+RŒ                   3U/S9-   [3        U35      5        Mê     [•        TTU35        U3 Vs/ s H  n[w        UR,                  U5      PM     n:nU: Vs/ s H  nURz                  SY:X  d  M  UPM     n;nU: Vs/ s H  nURz                  SZ:X  d  M  UPM     n<nU1 Vs/ s H  nURz                  S[:X  d  M  UPM     n=nU3(       a2  [—        S\ U: 5       5      (       d  [u        S][3        U35       S5U$ S^35      eU;U<-    Hî  n[         R˜                  " [›        UR,                  5      U5      n>[         Rœ                  " U>5       H  n?URž                  RY                  U?5        M      [¡        UR,                  U5      n@U@(       a  URž                  RY                  W@5        […        UR,                  S_S5      (       d  M»  URž                  RY                  S`UR,                  R¢                   Sa35        Mð     U!U;-  n!U"U<-  n"U#U=-  n#[        U$ Sb[3        U;5       Sc[3        U<5       Sd35        U4(       d$  [         RV                  " US[3        U;5       ST35        TURY                  U5        [[        UU!-   UU"-   UU#-   UU5        GM_     TL(       d  TM(       a  [        SeTL SfTM Sg35        SSS5        UW!-   nAUW"-   nBUW#-   nCSR1                  UU4Sh j[         5       5      nD[3        UA5       SiUD=(       d    Sj S3nETQ(       a(  SR1                  UQ4Sk j[         5       5      nFWES5UF Sl3-  nE[3        WA5      [3        WB5      -   [3        WC5      -   n8 [¥        UAUBUSS9  [§        SnSWEU8U85        [        So[3        WA5       Sp[3        WB5       Sq35        [        Sr5        [        UASs SC9 H¬  n[        St[�        UR,                  R¨                  5      Su Sv[�        URª                  R¬                  5      Su SNUR,                  R®                   S5UR,                  RŠ                   S5UR,                  RŒ                   S5UR,                  R°                   35        M®     g! [         a  n[        S	U S
35         SnAGN'SnAff = fs  snf ! [         a    / / / S4u  pïnn GN•f = fs  snf s  snf s  snf s  snf ! [         a$  n[        S"U 35        [        S#5         SnAGNASnAff = fs  snf s  snf s  snf s  snf s  sn+f s  sn+f ! [         a     GN0f = f! U-Rs                  5         f = fs  sn+f s  snf s  snf s  snf s  snf s  snf ! , (       d  f       GNž= f! [         a  n[        SmU 35         SnAGNSnAff = f)wað  The daily read and price. By design (Steven 2026-06-15) the scheduled run
prices on Cazana only, so the page is always full without depending on a live
Glass's session. Glass's is added on demand by glass_pass() when Steven
clicks Run Glass's checks. Pass use_glass=True to also try Glass's inline.

platforms restricts which auction houses are actually read this time (Mark
2026-08-20: the cockpit's Run modal lets him refresh just one, so changing
a setting for Dealer Auction alone does not force a slow repull of Motorway,
Carwow and Auction4Cars too). None or every key in PLATFORM_KEYS means the
normal full run, unchanged. A smaller set makes this a PARTIAL run: cars
from the platforms left out are carried forward exactly as they were on the
last saved run (same status, pricing, flags), not re read, not re valued,
not re assessed, only the chosen platform(s) go through the real pipeline
below. The DealerKit stock read is a separate concern (its own on demand
button) and always runs regardless of this selection.

Each selected platform is read, gated, valued and written to the cockpit
in its own turn, not all at once (Mark 2026-08-24: so the cars already
done are viewable right away rather than waiting on every platform in the
run to finish, see CLAUDE.md). test_limit, a dev only testing aid, is
applied PER PLATFORM now rather than once across the whole combined pool
(valuing the N cheapest of each platform read, not the N cheapest
overall), an accepted side effect of valuing platform by platform.zCleared z, valuation cache entries older than 48 hoursz0 listing detail cache entries for cars long goneF)Ú
standaloneÚappliedúDealer OS: z# change(s) collected before the runz5Dealer OS: could not collect changes before the run (ú), carrying onNzUnknown platform(s) z, expected some of TzThe last run was for zA, an earlier day: none of its cars are carried into this run for rG  rE  c              3   óD   >#   • U  H  oT;   d  M
  [         U   v •  M     g 7frŸ  ©ÚPLATFORM_SOURCE)rW  rp   r3   s     €r0   rX  Úrun.<locals>.<genexpr>  s   øé € ÐU²m°ÈHÁ}Ó,œ/¨!Ö,²mùó   ƒ	 � zPartial run: refreshing z only. Carrying forward zD cars from the other platforms exactly as they were in the last run.z!BidBrain daily run for sale date rÇ   r"   ÚstockzReading DealerKit in stock list)Úwith_recordszDealerKit in stock: ú vehicles, z	 models, c              3   óL   #   • U  H  oR                  S 5      c  M  Sv •  M     g7f)Úpurchase_costNr9   )r+   )rW  r   s     r0   rX  rœ  #  s   é € ÐVª 1·5±5¸Ó3IŸQ™Qªùs   ‚$›	$z with a purchase costzDealerKit stock read failed: zBCarrying on with the last good snapshot, stock flags may be stale.r  r   c            	      ó¶  • SSK n [        R                  R                  [        R                  R	                  [        R                  R                  [        5      5      S5      n[        R                  R                  U5      (       d  g U R                  [        R                  U/SSS[        R                  R	                  U5      S9nUR                  =(       d    UR                  =(       d    S	R                  5       R!                  5       n[        S
U(       a  US   OS 35        UR"                  S:H  $ ! [         a  n[        SU 35         SnAgSnAff = f)a{  Log Cazana back in mid run, once, the moment its breaker trips
(Steven 2026-09-02, Step 2 of the review: the held pile's
biggest cause was Cazana's session dying partway through a run,
three runs in the log, after which every remaining car was
held and, on a one day Motorway auction, lost for good). The
same keychain login the 17:00 launchd job runs, as a
subprocess because auto_login_cazana opens its own Playwright
and this run is already inside one. Returns True only when the
login script itself reports success; anything else, including
no saved credential on this Mac, is False and the breaker
stays tripped exactly as before.r   Nzauto_login_cazana.pyFTr[  ©Úcapture_outputÚtextrŒ   Úcwdz!  Cazana re-login could not run: rX   z  Cazana re-login: éÿÿÿÿz	no output)Ú
subprocessrì   rí   rI  r9  ÚabspathÚ__file__r1  ÚrunÚsysÚ
executabler¦   r¨   ÚstdoutÚstderrr  Ú
splitlinesÚ
returncode)r©  Úscriptr   rK  Útails        r0   Ú_cazana_reloginÚrun.<locals>._cazana_reloginI  sÿ   € ó Ü—W‘W—\‘\¤"§'¡'§/¡/´"·'±'·/±/Ä(Ó2KÓ"LÐNdÓeˆFÜ—7‘7—>‘> &×)Ñ)ØðØ—N‘N¤C§N¡N°FÐ#;ÈDÐW[Ø+.´B·G±G·O±OÀFÓ4Kð #ð M�ð
 —H‘H×. §¡×.¨B×5Ñ5Ó7×BÑBÓDˆDÜÐ'¶D¨¨Rª¸kÐ'JÐKÔLØ—<‘< 1Ñ$Ð$øô ó ÜÐ9¸!¸Ð=Ô>Üûðús   Â	A D6 Ä6
EÅ EÅEc           	      óâ   >• T(       a  g [         R                  " TXUS9nSmUS 4$ ! [         a=  nTS-  mTT:¼  a  Sm[        ST SU 35        O[        ST S	T S
U 35         S nAgS nAff = f)Nrý   ©Úexpectr   r9   Tz  GLASS'S IS DOWN after z5 errors in a row, pricing on Cazana alone from here. z  Glass's lookup glitch (r–   ú), carrying on. )r   Úlookupr¦   r¨   )	rW   r  r¹  rq   rK  Ú
DEAD_AFTERÚ
glass_deadÚglass_failsr©   s	        €€€€r0   Ú
_try_glassÚrun.<locals>._try_glassc  s›   ø€ æØ!ð"Ü—L’L  C¸Ñ@�Ø�Ø˜$�w�øÜó "Ø˜qÑ �Ø *Ó,Ø!%�JÜÐ4°[°Mð B@Ø@A¸sðDõ Eô Ð5°k°]À!ÀJÀ<ÐO_Ð`aÐ_bÐcÔdÜ!ûð"ús   ‹' §
A.±3A)Á)A.c           	      ó¾  >• T	(       a  g [         R                  " TXUS9u  p4Sm
X44$ ! [         aª  nT
S-  m
T
T:¼  a�  T(       df  Sm[        ST
 S35        T" 5       (       aI  Sm
 [         R                  " TXUS9u  p4[        S5        X44s S nA$ ! [         a  nTm
Un S nAOS nAff = fSm	[        S	T
 S
U 35        O[        ST
 ST SU 35         S nAgS nAff = f)Nrý   r¸  r   r9   Tz  Cazana failed z3 times in a row, logging it back in and retrying...z  Cazana is back.z  CAZANA IS DOWN after z6 errors in a row, pricing on Glass's alone from here. z  Cazana lookup glitch (r–   rº  )r   r»  r¦   r¨   )rW   r  r¹  rq   ÚurlrK  rL  r¼  rµ  Úcazana_deadÚcazana_failsÚcazana_relogin_triedr©   s          €€€€€€r0   Ú_try_cazanaÚrun.<locals>._try_cazanau  s  ø€ æØ!ð"ÜŸš q¨#¸vÑF‘�Ø �Ø�v�øÜó "Ø Ñ!�Ø :Ó-ö
 0Ø/3Ð,ÜÐ 0°°Ð>qÐrÔsÙ*×,Ñ,Ø+,˜Lð'Ü)/¯ª°q¸#ÈvÑ)V¡ Ü %Ð&9Ô :Ø'( v¥øÜ#,ó 'Ø/9 Ø$&¥ûð'úð #'�KÜÐ3°L°>ð BAØABÀðEõ Fô Ð4°\°NÀ!ÀJÀ<ÐO_Ð`aÐ_bÐcÔdÜ!ûð1"ús>   ‹( ¨
C²1CÁ$%BÂ	CÂ
B'ÂB"ÂCÂ"B'Â'+CÃCc           
      ó´   >• Uu  pV[         R                  " X#XU5      u  pxn	U(       a  TS-  mX‰4$ TS-  mU" X#U5      u  p«[         R                  " X#XXjUS9  X«4$ )av  Reuse a Glass's or Cazana value read within the last 48 hours
for this exact reg, mileage and source, instead of repeating the
lookup (the same car can legitimately reappear across days, most
of all an Auction4Cars auction still running). Falls through to
the real lookup on a miss and saves whatever it gets back
(including a confirmed no match), so a persistently unmatched reg
is not re queried every day either. Returns (value, url); url is
Cazana's own real result page (Mark 2026-08-25, "add a link on
the card on the word Cazana"), cached alongside the value so a
cache hit still carries a real deep link, None for Glass's.r9   )rÂ  )r   Úget_cached_valuationÚsave_cached_valuation)rc   Úlive_fnrW   r  r¹  r  r  ÚhitÚcached_valueÚ
cached_urlÚvaluerÂ  Ú
cache_hitsÚcache_missess               €€r0   Ú_cached_or_liveÚrun.<locals>._cached_or_live—  sq   ø€ ð !‰KˆDÜ,.×,CÒ,CÀCÐRXÐ`eÓ,fÑ)ˆC˜zÞØ˜a‘�
Ø#Ð/Ð/Ø˜AÑˆLÙ  ¨vÓ6‰JˆEÜ×$Ò$ S°6ÀÐSVÒWØ�:ÐrE   c                 ó  >• U R                   U R                  4nT" STU R                  U R                  U5      u  U l        nT" STU R                  U R                  U5      u  U l        U l        U R                  =(       d    SU l        U R                  (       aô  U R                  b  U R
                  cÙ  [        SU R                   SU R                   35        U R                  cC  T(       d<  T" STU R                  U R                  U5      u  U l        nU R                  b  SU l	        U R
                  cV  T(       dN  T" STU R                  U R                  U5      u  U l        nU R
                  b  SU l	        U=(       d    SU l        g g g g g g )Nr   r   rX   r•   z+: retrying valuation on the original plate T)
r  r  rW   r  rU  r\  Ú
cazana_urlrY   r¨   rð  )	rÝ   r¹  rj   rÕ  rÒ  rÆ  r¿  rÃ  r½  s	       €€€€€r0   Ú
_value_oneÚrun.<locals>._value_one­  sM  ø€ Ø—f‘f˜aŸg™gÐ&ˆFÙ /°¸ÀQÇUÁUÈAÏIÉIÐW]Ó ^ÑˆAŒN˜AÙ,;¸HÀkÐST×SXÑSXÐZ[×ZcÑZcÐekÓ,lÑ)ˆAŒO˜Qœ\ØŸ<™<×-¨2ˆAŒLØ�}�} !§.¡.Ñ"8¸A¿O¹OÑ<SÜ˜˜1Ÿ5™5˜'Ð!LÈQÏ]É]ÈOÐ\Ô]Ø—>‘>Ñ)¶*Ù(7¸ÀÈQÏ]É]Ð\]×\eÑ\eÐgmÓ(nÑ%�A”N AØ—~‘~Ñ1Ø*.˜œØ—?‘?Ñ*¶;Ù2AØ  +¨q¯}©}¸a¿i¹iÈó3QÑ/�A”O Zà—‘Ñ2Ø*.˜œØ'1×'7°R˜�ð 3ð 4?Ð*ð =Tˆ}rE   r'   rî  úReading z stockr   z: read failed, falling back to z= cars from the last known good run. Re login when convenient.zRead failed, showing z cars from the run beforezSuppressed r  z" cars (hidden or walked away from)z Motorway car pagesr†   r9   zMotorway car pages, r”   c              3   óJ   #   • U  H  oR                   (       d  M  S v •  M     g7fr”  )rù  ©rW  rÝ   s     r0   rX  rœ    s   é € Ð!Mª]¨¿n½n§!¡!ª]ùó   ‚#š	#rQ  r)  z&Motorway car detail read failed: only a   cars returned a keeper date. The under 6 months owner check, the known issue check and the private plate valuation fallback could not run, so the list is not safe to show. Not shipping it. Re run when the Motorway session is healthy (python3 login.py motorway manual, then Run now).)rº   r»   c                 ó@   • U R                   R                  =(       d    S$ ©Nr   rÉ   rÊ   s    r0   rË   Úrun.<locals>.<lambda>  s   € ¨A¯E©E¯M©M×,>¸QÐ,>rE   rÍ   rU  z pass the full gate and bansz, valuing the z cheapest (test mode)z, valuing them now)ÚCounterrâ   ÚfailureséZ   é   r•   )rX   r   úRead z2 cars and none passed the gate. Commonest reason, ÚvaluingzValuing ú carszK  Both valuation sources are down, the rest stay unpriced. Stopping loudly.ú
: Glass's z	, Cazana z	: valued rº   r»   r¼   c              3   ó8   #   • U  H  oR                   v •  M     g 7frŸ  ©rã   rV  s     r0   rX  rœ  R  s   é € Ð#>²v°!§I¦I²vùó   ‚zPriced 0 of z§ cars, both valuation sources look down. Keeping the previous list rather than overwriting it with an empty one. Re login Cazana (python3 login.py cazana) and Run now.rð  rñ  rò  z done: rç  ú held.zValuation cache: z	 reused, z looked up freshc              3   óD   >#   • U  H  oT;   d  M
  [         U   v •  M     g 7frŸ  rš  )rW  rp   Ú	read_keyss     €r0   rX  rœ  ‰  s   øé € ÐV²m°ÈIÁ~Ó,œ/¨!Ö,²mùr�  z shortlisted. Read Únothingc              3   óD   >#   • U  H  oT;   d  M
  [         U   v •  M     g 7frŸ  rš  )rW  rp   Ú	fell_backs     €r0   rX  rœ  Œ  s   øé € ÐXº°1ÈiÉÓ.œO¨AÖ.ºùr�  z fell back to the run before.z$Dealer OS push failed unexpectedly: r¬  z
Shortlist: z priced. Held: z+. Pages written: cockpit.html, hidden.html.zNServe them with python3 serve.py, then open http://localhost:8765/cockpit.htmlc                 ó.   • U R                   R                  $ rŸ  rÉ   rÊ   s    r0   rË   rÞ  �  s   € ¨Q¯U©U¯]ª]rE   z
  reserve z>8z
  max bid )Yr   rÎ   Úprune_old_valuation_cacher¨   Úprune_old_listing_detail_cacheÚdealer_os_pull_passr+   r¦   r)   r*   rw  r@  rá  r1   r5   r¶   rJ   rD   r>   rA   rO   r›  r\   rc   rI  rš   r<   r·   r   rc  r™   r   Úread_in_stockÚrecord_stock_snapshotÚsave_dealerkit_stockrb  Úvaluesr±  rÏ   rÐ   rÑ   rÒ   rÓ   Úrecord_run_statusrH  rê  r   rW   r  Ú
mech_issuer   r�   rž   rŸ   r    r   rø  r§   r^  rÔ   rÕ   rk   rÖ   rM   Úcollectionsrß  rZ   rF  Úmost_commonr  r  r   rU  r\  r´   r½  rÃ  r]   rú  rl   rg   rY   Údealer_os_push_passÚ_finishr_   rã   r3  r  Ú
derivative)WÚ
test_limitrN   Ú	use_glassÚ	platformsÚprunedÚpruned_detailsÚgotrK  r,   Úunknownr-   r/   r4   rè  Ú
prev_shortÚ	prev_heldÚprev_rejrH   Úcarried_shortlistÚcarried_heldÚcarried_rejectedrp   Úexcluded_sourcesro   ÚnamesÚ	carried_nrI   r  ÚpairsÚrecordsrÖ  r…  ÚreadersÚall_shortlistÚall_heldÚall_rejectedÚsrcrû  Úfb_shortÚfb_heldÚfb_rejÚstalerv  rÝ   Úmw_candidatesÚdctxÚdpager±   rý  rÞ   Úgate_passersÚto_valueÚnone_passedrß  râ   r   r  Útoprþ  Úfresh_shortlistÚ
fresh_heldÚfresh_rejectedr  r  r  rº   r»   r¼   Ú	done_bitsrÌ  Úfb_bitsr¼  rÒ  rµ  rÆ  r¿  rÐ  rÑ  rÃ  rÄ  rÅ  rï  r½  r¾  r©   rì  r3   sW                                                                          @@@@@@@@@@@@@@@@r0   r¬  r¬  ±  s%  ÿÿ€ ô0 ‡J‚J„LÜ×)Ò)Ó+€FÞÜ�˜˜Ð LÐMÔNÜ×6Ò6Ó8€NÞÜ�˜Ð(Ð(XÐYÔZðYÜ!¨UÑ3ˆÞ�3—7‘7˜9×%Ñ%Ü�K  I¡Ð/Ð/RÐSÔTö #,”�I”´³€IØœ#œmÓ,Ñ,€GÞÜÐ/´°w³Ð/@Ð@SÔTaÐSbÐcÓdÐdô
 %Ó&€KÜ  Ó8€Hð ˜rÐ€IˆyÝ#0ÓO¢=˜C·O±OÀCÈ×4N—C¡=€LÐOô ˜h¨Ó4€GðKÜ:E»-Ñ7ˆ
˜x¨ð 9;¸BÀÐ%5�|ÐÞ�9Ñ$Ü& ~´|ÄH×DUÑDU×DYÑDYÓD[Ó7\Ó]ˆ	Ø×Bœ\¬(×*;Ñ*;×*?Ñ*?Ó*AÓB€Iö 	–i¦8Ô5FÀ~ÐW`×5aÑ5aÜÐ% nÐ%5ð 64Ø4=°;¸aðAô 	Bà*,¨b°"˜x�IÞñ 9EÓZº°1ÈÐQYÑHYÓ.œO¨AÔ.¹ÐÐZÙ(2ÓWª
 1°a·e±e·l±lÐFVÑ6VŸQ©
ÐÐWÙ#,ÓQ¢9˜a°·±·±Ð@PÑ0PŸ¡9ˆÐQÙ'/ÓT¢x !°1·5±5·<±<ÐCSÑ3SŸA¡xÐÐTØ—	‘	ÔUµmÓUÓUˆÜÐ)Ó*¬S°Ó->Ñ>ÄÐEUÓAVÑVˆ	ÜÐ(¨¨Ð/GÈ	À{ð SOð Pô 	Qô �M‰M×'Ñ'¨	Ó2€EÜ	Ð-¨i¨[¸Ð
;Ô<ä	Ô	˜aô  Ð 1×2Ñ2Ü�gÐ@ÔAð\Ü)2×)@Ò)@ÀÐQUÑ)VÑ&�˜˜wÜ×(Ò(¨°	Ô:Ü×'Ò'¨Ô0ÜÐ,¬S°·±³Ó-AÐ,BÀ+ÌcÐRXËkÈ]ÐZcÜÑV©ÓVÓVÐWÐWlðnô oð< #”]ˆ
ØˆØˆ
ØˆØˆØ$ÐØˆ
Øˆò	%÷4	"ð 	"÷$ 	"ò  	"öD	÷,	8ñ 	8ô$ ×#Ò#Ó%ˆÜ-¼Ü#5ÔH[Ü /ñ1ˆð 13°B¸ �xˆå,Ó>š}˜!°°X±—A™}Õ>ˆCÜ! #Ñ&ˆCÜ�i 8¨C¨5°Ð!7Ô8Ø�jÓ Ø˜s’| A yÓ1’à˜s’| A yÓ1�Ø’<Ù+5ÓMª: a¸¿¹¿¹ÈÑ9L§©:�HÐMÙ*3ÓKª) Q°q·u±u·|±|ÀsÑ7JŸq©)�GÐKÙ)1ÓIª A°Q·U±U·\±\ÀSÑ5HŸa©�FÐIØ! XÑ-�MØ Ñ'�HØ  FÑ*�LÜ ›M¬C°«LÑ8¼3¸v»;ÑF�EÜ˜S˜EÐ!@ÀÀð HYð Zô [ô ×(Ò(Ø˜UØ/°¨wÐ6OÐPôRð ×$Ñ$ SÔ)Ü%Ð&7¸-Ñ&GÈÐX`ÑI`Ø&6¸Ñ&EÀuÈiôYâØ—:˜2ˆDä˜“YˆFÙ#ÓIšt˜!¤r§|¢|°A·E±EÓ':À&Ñ'H—A™tˆDÐIØœ˜D›	×!Ü˜ F¬S°«YÑ$6Ð#7°q¸¸Ð=_Ð`Ôaà�jÔ ñ -1Ó aªD q´MÀ!ÀU×4K£ÐTU×T`ÕT`§©D�Ð aß Ü˜i¨8´C¸Ó4FÐ3GÐGZÐ)[Ø¤ ]Ó!3ô5ä"×6Ò6°q¸*ÈtÑT�Dð
%Ø15·· §
¡
¨1¢ÀÇÁÃ˜Ü$-¨m¸QÖ$?™D˜A˜qð%Ü (× 5Ò 5°e¸QÔ ?ô & iÐ3GÈÀsÈ$ÌsÐS`ÓOaÐNbÐ1cØ&'¬¨]Ó);ö=ñ %@ð Ÿ
™
œô "Ñ!M©]Ó!MÓM�GÜ˜=Ó)¨QÓ.°7¸SÄ3À}ÓCUÑ=UÓ3UÜ*ØDÀWÀIÈTÜ" =Ó1Ð2ð 3XðYóZð Zñ 7;Ó;²d°œ7 1 eÖ,±dˆKÐ;Ü˜+Ô&Ù'2ÓX¢{ !°a·h±hÐBWÑ6WŸA¡{ˆLÐXØ×ÑÑ">ÐÑ?Þ4>�| K ZÑ0ÀLˆHÜ�B�s�e˜2œc ,Ó/Ð0Ð0LÐMÞNX�~¤c¨(£m _Ð4IÑJÐ^rñtô uä˜{Ó+×@°LÔ0@ˆKÞõ 0Ù!›)�Û$�AÜ% a¨°DÓ9×_¼WÀQÈ
ÐTXÓ=Y×_Ð]_Ò_˜Ø¤ A£ s¨ Ó,°Ñ1Õ,ó `ñ %ð $×/Ñ/°Ö2‘D�A�qÜ˜B˜q˜c ¤c¨+Ó&6Ð%7°r¸!¸Ð=Ö>ñ 3ö 4;�g×)Ñ)¨!Ó,¨QÒ/À�Ü×$Ò$Ø˜ØœC Ó,Ð-ð .)Ø),¨Q©¨°´S¸Ó5EÐ4FÀbÈÈQÉÈðRôSô
 �i 8¬C°«M¨?¸!¸C¸5ÀÐ!FÈÌ3ÈxË=ÔYÜ! (Ö+‘��1Ø—E‘E�Þ¦+ÜÐgÔhÙÙ˜1”Ü˜˜1Ÿ5™5˜'  1§6¡6 (¨!¨A¯G©G¨9°J¼vÀaÇnÁnÓ?UÐ>VÐV_Ô`fÐgh×gvÑgvÓ`wÐ_xÐyÔzÜ˜)¨ u¨I°a¸!±e°W¸DÄÀXÃÀÈrÐRS×RWÑRWÐQXÐXYÐZ[×Z`ÑZ`ÐYaÐabÐcd×cjÑcjÐbkÐ%lØ˜a™%¤ X£ö0ñ ,ô # 1 hÔ/á5=Ó>²X°”g˜aŸe™e UÖ+±XˆFÐ>Ù*0ÓLª& Q°A·H±HÀÑ4KŸq©&ˆOÐLÙ%+ÓB¢V ¨q¯x©x¸6Ñ/AŸ!¡VˆJÐBÙ)4ÓOª A¸¿¹ÀJÑ8NŸa©ˆNÐOö ¤Ñ#>±vÓ#>× >Ñ >Ü"Ø"¤3 x£= /°°3°%ð 8Xð YóZð Zð
 % zÔ1�Ü×(Ò(¬°Q·U±UÓ);¸YÓG�Ü×0Ò0°Ö6�DØ—G‘G—N‘N 4Ö(ñ 7ä)¨!¯%©%°Ó;�ÞØ—G‘G—N‘N 4Ô(Ü˜1Ÿ5™5 /°5×9Ó9Ø—G‘G—N‘NÐ%bØ&'§e¡e×&7Ñ&7Ð%8Ð8Jð$Lö Mñ 2ð ˜_Ñ,ˆMØ˜
Ñ"ˆHØ˜NÑ*ˆLÜ�S�E˜¤ _Ó!5Ð 6°nÄSÈÃ_ÐDUÐU[Ð\Ô]ö Ü×$Ò$ S¨$´3°Ó3GÐ2HÈÐ0NÔOØ×Ñ˜SÔ!ô
 Ð/°-Ñ?ÀÐPXÑAXØ.°Ñ=¸uÀi÷QñW ?ö\ žÜÐ% j \°¸<¸.ÐHXÐYÔZ÷C 
ðF " MÑ1€IØ˜(Ñ"€DØ ,Ñ.€Hð —	‘	ÔVµmÓVÓV€IÜ�Y“Ð Ð 3°I×4JÀÐ3KÈ1ÐM€GÞØ—)‘)ÔX½ÓXÓXˆØ�Q�w�iÐ<Ð=Ñ=ˆÜˆI‹œ˜T›Ñ"¤S¨£]Ñ2€Að:Ü˜I t¨YÀ5ÒIô ˆE�4˜ ! QÔ'Ü	ˆMœ#˜i›.Ð)¨¼¸T»¸ÐCnÐ
oÔpÜ	Ð
ZÔ[Ü�IÑ#:Ô;ˆÜ�
œ6 !§%¡%§-¡-Ó0°Ð4°J¼vÀaÇiÁi×FWÑFWÓ?XÐY[Ð>\Ð\^Ð_`×_dÑ_d×_iÑ_iÐ^jÐjkÐlm×lqÑlq×lvÑlvÐkwÐwxÐyz×y~Ñy~÷  zEñ  zEð  yFð  FGð  HI÷  HMñ  HM÷  HXñ  HXð  GYð  Zö  	[ò <øôM ó YÜÐEÀaÀSÈÐW×XÒXûðYüò" Pøô  ó KØ:<¸bÀ"ÀdÐ:JÑ7ˆ
˜x«ðKüò( [ùÚWùÚQùÚTøô* ó \ÜÐ5°a°SÐ9Ô:ÜÐZ×[Ò[ûð\üòL ?ùò  NùÚKùÚIùò. Jùò !bøô $-ó %Ú $ð%ûð
 Ÿ
™
�üò <ùâXùò^ ?ùÚLùÚBùÚO÷e	 
Ö	ûô@ ó :ÜÐ4°Q°CÐ8×9Ò9ûð:úsÒ  Á=8z= Ä*{$Å{$Å{) È$
|È2|É|	É'|	É3|Ê|Ê!|Ë|Í~+Í5B|Ð A<~+Ñ<
}	Ò
}	ÒA~+Ó}Ó:}Ô ~+Ô}Ô(}Ô.~+Ô4}Õ}ÕB'~+Ø(}Ø/}Ø5>~+Ù3}"Ú}"Ú}"Ú%A~+Û*A}8Ü-}'Ý*}8Ý.A"~+ß~ß&~+ß7~à~àJ~+ê( ~ë~+ë~ë&~ë,~+ë2~!ì
~!ì~+ì~&ì.~&ì4C7~+ð/C%~+ö7~= ú=
{!û{û{!û)|ü |ü
}ü"}ü;~+ý}ý!~+ý'
}5	ý1}8ý4}5	ý5}8ý8~
þ
!~+þ+
~:þ=
 ÿÿ c                  ó(  • SSK n Sn U R                  SSU/U R                  SS9n[        R                  " SUR                  5       5       Vs/ s H  n[        U5      PM     nn[        U5      S	:¼  a
  US   US
   4$  gs  snf ! [         a     gf = f)a  Where the screen Steven is using has its active window, so the Glass's
login window can open on the same screen as the cockpit rather than the
other monitor. Reads the position of the frontmost app's front window via
AppleScript. Returns (x, y) or None if it cannot be read.r   Nzptell application "System Events" to tell (first process whose frontmost is true) to get position of front windowÚ	osascriptz-erQ  )r°  rŒ   z-?\d+r  r9   )	r©  Úcheck_outputÚDEVNULLr›   ÚfindallÚdecoder  rš   r¦   )r©  r³  Úoutr  Únumss        r0   Ú_front_window_posr.  ¡  s®   € ó
 ðX€FðØ×%Ñ% {°D¸&Ð&AØ-7×-?Ñ-?Èð &ð Lˆä "§
¢
¨8°S·Z±Z³\Ô BÓCÒ B˜1”�A–Ñ BˆÐCÜˆt‹9˜‹>Ø˜‘G˜T !™WÐ%Ð%ð ð ùò Døô ó ØØðús$   ˆAB ÁA?Á"B Á?B Â
BÂBc                  ó  • Sn [         R                  R                  S5      nU(       a   S UR                  S5       5       u  p#X#4n U c
  [        5       n U (       a  SU S    SU S    3/$ S$ ! [        [
        4 a    Sn  N9f = f)a"  Chrome args to open a headed login window on the same screen as the
cockpit. The cockpit sends its screen position via BIDBRAIN_GLASS_WIN (no
macOS permissions needed); fall back to the frontmost window for a CLI run,
else let Chrome place it. Shared by the Glass's and Clickdealer windows.NÚBIDBRAIN_GLASS_WINc              3   ó8   #   • U  H  n[        U5      v •  M     g 7frŸ  )r  )rW  r  s     r0   rX  Ú"_login_win_args.<locals>.<genexpr>½  s   é € Ð7Ò$6˜q”C˜—F�FÒ$6ùré  Ú,z--window-position=r   r9   )rì   Úenvironr+   r  rw  Ú	TypeErrorr.  )ÚposÚenv_winrû   Úys       r0   Ú_login_win_argsr9  ´  s�   € ð
 €CÜ�j‰j�n‰nÐ1Ó2€GÞð	Ù7 G§M¡M°#Ô$6Ó7‰DˆAØ�&ˆCð �{ÜÓ!ˆÞ7:Ð   Q¡ ¨¨#¨a©&¨Ð2Ð3ÐDÀÐDøô	 œIÐ&ó 	ØŠCð	ús   ªA, Á,BÂ Bc                 óÄ  • SSK n[        R                  " U S[        5       S9nUR                  (       a  UR                  S   OUR                  5       n UR                  [        R                  S   S   SSS	9  SnSnXQ:  ap  [        UR                  5       H   n UR                  S
5      (       a  SnUn  OM"     U(       a  O/[        SSX-
   S35        UR                  S5        US-  nXQ:  a  Mp   UR                  [        R                  " S5      S9  U(       d  UR                  5         gX44$ ! [         a     NÄf = f! [         a     M·  f = f! [         a     NHf = f)aò  Open a headed Glass's window so Steven can log in, then save the session.
Returns (ctx, page) ready to value on once the valuation box appears (the
logged in signal), or (None, None) if he does not finish within the timeout.

The caller keeps the context open and runs the whole list on this one logged
in page, then closes it. That is the heart of the on demand Glass's design:
Glass's is touched only while Steven is freshly logged in, and as one
continuous session, not a fresh browser per car (which Glass's reads as
session churn and kills partway through the list).

The window is positioned on the same screen as the cockpit (the frontmost
window when the button is clicked) because the Studio has two monitors and it
was opening on the wrong one.r   NF©r‡   Ú
extra_argsr   Ú	login_urlrˆ   é N  rŠ   z#plateNumberInputTúglass-loginz3Waiting for you to log into Glass's in the window (ús left)r  rj  rý   )r=   r   Úopen_contextr9  rž   rŸ   r¡   r]  r¦   Úlistr“  r™   ÚsleepÚstorage_statern  r§   )r©   Ú	timeout_sr=   r¯   r°   ÚwaitedÚ	logged_inÚpgs           r0   Ú_glass_loginrI  Æ  sm  € ó Ü
×
Ò
˜q¨5¼_Ó=NÑ
O€CØŸ9Ÿ9ˆ3�9‰9�QŠ<¨#¯,©,«.€DðØ�	‰	”'—-‘- Ñ(¨Ñ5Ø/¸ð 	ñ 	@ð €FØ€IØ
Ó
Ü�s—y‘y–/ˆBðØ×$Ñ$Ð%8×9Ñ9Ø $�IØ�DÙñ :ñ "ö Øô 	�-ðØÑ(Ð)¨ð2ô	3ð 	�
‰
�1ŒØ�!‰ˆð# Õ
ð&Ø×Ñœw×1Ò1°'Ó:ÐÑ;ö Ø�	‰	ŒØØˆ9Ðøô= ó Ùðûô ó Úðûô ó Ùðús6   Á%D1 ÂEÃ3#E Ä1
D>Ä=D>Å
EÅEÅ
EÅEc                  ó  • [         R                  " 5         [        S5      (       d  [        R                  " S5      e [        5       u  pp#[        R                  R                  U5      nX-   n[        S[        U5       SU S35        [        5        n[        SS	5        [        U5      u  pxUc"  [        SS
S5        [        S5         SSS5        g S
n	[        SS[        U5       S3S[        U5      5        [!        U5       GH–  u  p«UR"                  nU	(       a    GOUR$                  bB  SUl        [        SSU
S-    S[        U5       SUR(                   3U
S-   [        U5      5        Mk  UR*                  UR,                  4n [.        R0                  " XŒR(                  UR2                  US9nUcM  UR4                  (       a<  U	(       d5   [.        R0                  " XŒR4                  UR2                  US9nUb  SUl        U	(       d  SUl        Ub  Xìl        [        SUR(                   SUR*                   SUR,                   S[9        UR$                  5       35        [        SSU
S-    S[        U5       SUR(                   3U
S-   [        U5      5        GM™     UR;                  5         [=        Xe5        SSS5        U Vs/ s H  n[?        UR"                  U5      PM     nn[A        U5        U Vs/ s H  o»RB                  S:X  d  M  UPM     n nU Vs/ s H  o»RB                  S:X  d  M  UPM     nnX-    Hî  n[         RD                  " [G        UR"                  5      U5      n[         RH                  " U5       H  nURJ                  RM                  U5        M      [O        UR"                  U5      nU(       a  URJ                  RM                  U5        [Q        UR"                  SS
5      (       d  M»  URJ                  RM                  SUR"                  R4                   S35        Mð     U RS                  S  S!9  [         RT                  " X-   U-   5        [W        XX#5        [        S"S#[        U5      [        U5      5        [Y        XX$U5        [        SSS$[        U 5       S%[        U5       S&3[        U5      [        U5      5        [        S'[        U 5       S%[        U5       S&35        g! [         a    [        R                  " S5      ef = f! [         a  nSn	[        SU 35        Sn SnAGN†SnAff = f! [         a     GNKf = f! UR;                  5         f = f! , (       d  f       GN³= fs  snf s  snf s  snf )(aÛ  On demand Glass's top up. The scheduled run prices every car on Cazana so
the page is always full. When Steven is on the app he clicks Run Glass's
checks, which opens a Glass's login window for him, then values the cars
already on the page with Glass's too and re prices with both valuations.

It only ever changes cars on the last run: never reads the platforms again,
never re gates. A car held back for want of any valuation can move onto the
shortlist once Glass's reads.r   zIGlass's checks are turned off in Settings > Connections. Nothing changed.z9No saved run to add Glass's to. Run the daily read first.NzGlass's pass for z cars from the last run (z)
r?  z Opening the Glass's login windowFz9Glass's login was not completed in time. Nothing changed.z5Glass's login not completed in time. Nothing changed.rä  zRunning Glass's checks on rå  r   TzGlass's checked r9   r”   rU  r¸  z1  Glass's stopped responding, stopping the pass. r•   r  ræ  rº   r»   rð  rñ  rò  c                 ó.   • U R                   R                  $ rŸ  rÉ   rÊ   s    r0   rË   Úglass_pass.<locals>.<lambda>\  rõ  rE   rÍ   ræ  z)Rebuilding the cockpit with Glass's addedzGlass's added. rç  rê  z
Glass's pass done. )-r   rÎ   rc  r   Ú
RunSkippedr¶   r¦   r¨   r>   r<   r·   rš   r   r™   rI  rý  r    r\   rU  r_  rW   r  r  r   Úvaluate_on_pager  rY   rð  r   r§   r´   rÔ   rÕ   rk   rÃ  r]   rú  rl   rH  rg   rZ   rÖ   ré  r¸   r¹   )rº   r»   r¼   rN   rI   rû  r©   r¯   r°   r½  r±   ro   rÝ   r¹  ÚvalrK  rþ  r  r  r  s                       r0   Ú
glass_passrP  û  s±  € ô ‡J‚J„LÜ ×(Ñ(Ü�oŠoÐiÓjÐjðÜ/:«}Ñ,ˆ	˜ô
 �M‰M×'Ñ'¨	Ó2€EØÑ€DÜ	Ðœc $›i˜[Ð(AÀ)ÀÈCÐ
PÔQä	Ô	˜aÜ�-Ð!CÔDÜ  “O‰	ˆØ‰;Ü�G˜UÐ$_Ô`ÜÐIÔJØ÷ 
Ð	ð*	ØˆJÜ�iÐ#=¼cÀ$»i¸[ÈÐ!NÐPQÔSVÐW[ÓS\Ô]Ü! $Ÿ‘�Ø—E‘E�ÞÚð —>‘>Ñ-Ø&*�A”OÜ˜iÐ+;¸AÀ¹E¸7À$ÄsÈ4ÃyÀkÐQSÐTU×TYÑTYÐSZÐ)[Ø !™e¤S¨£Yô0áØŸ&™& !§'¡'Ð*�ðÜ×/Ò/°·e±e¸Q¿Y¹YÈvÑV�Cð ‘; 1§=§=¾ðÜ#×3Ò3°D¿-¹-ÈÏÉÐ[aÑb˜Ø™?Ø.2˜AœOö "Ø&*�A”OØ‘?Ø%(”NÜ˜˜1Ÿ5™5˜'  1§6¡6 (¨!¨A¯G©G¨9°J¼vÀaÇnÁnÓ?UÐ>VÐWÔXÜ˜)Ð'7¸¸A¹°w¸dÄ3ÀtÃ9À+ÈRÐPQ×PUÑPUÈwÐ%WØ˜a™%¤ T£÷,ñI (ðN �I‰IŒKô 	˜qÔ'÷q 
ñz .2Ó2ªT¨Œg�a—e‘e˜UÖ#©T€FÐ2Ü�&ÔÙ"Ó>šF�q§h¡h°+Ñ&=—™F€IÐ>ÙÓ4’v�!§¡¨VÑ!3�A‘v€DÐ4ØÔˆÜ× Ò ¤¨Q¯U©UÓ!3°YÓ?ˆÜ×(Ò(¨Ö.ˆDØ�G‰G�N‰N˜4Ö ñ /ä! !§%¡%¨Ó3ˆÞØ�G‰G�N‰N˜4Ô Ü�1—5‘5˜/¨5×1Ó1Ø�G‰G�N‰NÐZØŸe™e×/Ñ/Ð0Ð0BðDö Eñ ð ‡N�NÑ.€NÑ/Ü×Ò˜	Ñ(¨8Ñ3Ô4Ü�	 Ô5ÜˆiÐDÄcÈ$ÃiÔQTÐUYÓQZÔ[Ü� (°9Ô=ÜˆG�T˜_¬S°«^Ð,<¸NÌ3ÈtË9È+ÐU[Ð\Ü�‹I”s˜4“yô"ä	Ð!¤# i£.Ð!1°ÄÀDÃ	¸{È&Ð
QÕRøôu ó Ü�oŠoÐYÓZÐZðûôF !ó Ø!%�JÜÐMÈaÈSÐQÔRØ–Cûðûô %ó Úðûð �I‰I�Kú÷e 
Ö	üòz 3ùâ>ùÚ4s�   ½S1 Â6U%ÃB4UÆ*TÆ1UÇ4T?ÈBUÊU%Ë U7Ë6U<ÌU<ÌVÌ0VÓ1!TÔ
T<ÔT7Ô1UÔ7T<Ô<UÔ?
UÕ	UÕUÕUÕU"Õ"U%Õ%
U4c                  óþ  • SSK n SSKnSSKJn  [        R
                  " 5         U R                  R                  5       n[        R                  S   S    SUR                   SUR                   SUR                   3n[        S	5        Sn[        5        n[        S
S5        [        R                   " US[#        5       S9n UR$                  (       a  UR$                  S   OUR'                  5       n UR)                  USSS9  Sn	U	S:  a•   UR-                  5       n
SU
;   a  U
nO{ U" UR.                  5      R0                  nSU;   a'  SU;  a!  SUR.                  ;  a  UR)                  USSS9  [        S
SSU	-
   S35        UR3                  S5        U	S-  n	U	S:  a  M•   UR5                  [        R6                  " S5      S9  UR9                  5          SSS5        U(       d  [;        SSS5        [        S5        g[<        R>                  " U5      u  pÍU(       d  [;        SSS5        [        S5        g[        S S![A        URC                  5       5       S"3S#S#5        [        RD                  " XÓRG                  5       5        [I        5       u  pïnn[K        XïUU R                  RM                  U5      U5        [;        SS$[A        URC                  5       5       S%3S#S#5        [        S&[A        URC                  5       5       S'[O        U5       S(35        g! [*         a     GNf = f! [*         a    Sn
 GNf = f! [*         a     GNÕf = f! [*         a     GN‘f = f! UR9                  5         f = f! , (       d  f       GN = f))aR  On demand Clickdealer stock refresh, the fallback for when the scheduled
read has been failing because the login expired. Opens a Clickdealer window
on the Studio so Steven can log in if needed, reads the in stock list,
snapshots it, and rebuilds the cockpit so the gap and in stock flags are
current. Keeps the valuations, no re valuing.r   N©Úurlparser   rN  z
&date_day=z&date_month=z&date_year=zClickdealer stock passúclickdealer-loginzOpening the Clickdealer windowFr;  rˆ   é`ê  rŠ   é,  rX   zMake / ModelÚmyclickdealerúid.clickdealerÚ
stock_listrQ  z)Waiting for you to log into Clickdealer (r@  r  rj  z=Stock not read, the login was not completed. Nothing changed.z7Clickdealer login/stock not completed. Nothing changed.z5Report read but no stock rows found. Nothing changed.ú&No stock rows parsed. Nothing changed.rž  rã  ú vehicles, updating the pager9   Tú vehicles in stockzClickdealer stock refreshed: r   ú models.)(r>   r=   Úurllib.parserS  r   rÎ   r<   rI   r   r]  rC   Úmonthr  r¨   r   r™   rA  r9  rž   rŸ   r¡   r¦   re  rÂ  ÚnetlocrC  rD  rn  r§   rý  r   Úparse_stockrb  r÷  rõ  r@   r¶   r¹   r·   rš   )ry  Ú_trS  rI   rÂ  r  r©   r¯   r°   rF  ÚhtmlÚhostr  r  rº   r»   r¼   rN   s                     r0   Úclickdealer_passre  f  s"  € ó ÛÝ%Ü‡J‚J„LØ�H‰H�N‰NÓ€EÜ�m‰m˜MÑ*¨;Ñ7Ð8Ø˜Ÿ	™	�{ ,¨u¯{©{¨m¸;ÀuÇzÁzÀlðT€Cä	Ð
"Ô#Ø
€CÜ	Ô	˜aÜÐ%Ð'GÔHÜ×"Ò" 1¨uÄÓARÑSˆð"	Ø#&§9§9�3—9‘9˜Q’<°#·,±,³.ˆDðØ—	‘	˜#Ð*<Àe�	ÑLð ˆFØ˜3“,ðØŸ<™<›>�Dð " TÓ)Ø�CØðÙ# D§H¡HÓ-×4Ñ4�DØ&¨$Ó.Ð3CÈ4Ó3OÐT`Ðhl×hpÑhpÓTpØŸ	™	 #Ð2DÈe˜	ÑTô Ð-ØEÀcÈFÁlÀ^ÐSZÐ[ô]à—‘˜”Ø˜!‘�ð) ˜3•,ð,Ø×!Ñ!¤w×'9Ò'9¸-Ó'HÐ!ÑIð �I‰I�K÷K 
öN Ü�˜uÐ&eÔfÜÐGÔHØÜ×+Ò+¨CÓ0�M€FÞÜ�˜uÐ&]Ô^ÜÐ6Ô7ØÜˆg˜œs 6§=¡=£?Ó3Ð4Ð4PÐQÐSTÐVWÔXÜ×Ò˜U§O¡OÓ$5Ô6ä+6«=Ñ(€I�X˜yÜ� (¨C¯H©H×,BÑ,BÀ9Ó,MÈyÔYÜˆM˜4¤C¨¯©«Ó$8Ð#9Ð9KÐ!LÈaÐQRÔSÜ	Ð)¬#¨f¯m©m«oÓ*>Ð)?¸{Ì3ÈvË;È-ÐW_Ð
`Õaøô_ ó Úðûô !ó Ø“Dðûô !ó Úðûô ó Úðûð �I‰I�Kú÷K 
Ö	ús¯   Â+M-Ã 0MÃ1LÄMÄL#Ä	MÄ%A	L6Å./MÆ #MÇM-Ì
L ÌMÌL Ì MÌ#L3Ì/MÌ2L3Ì3MÌ6
MÍ MÍMÍMÍ
MÍMÍMÍMÍM*Í*M-Í-
M<c           
      óâ  ^ ^^^^^• SSK nSSKnSSKmUR                  R	                  UR                  R                  [        5      5      n	U4S jmUUU UU4S jn
U(       a  S O[        nU" SSU S35        U
" S	5      u  pÍnU(       a  XÍS	4$ UR                  5         U" SS
U S35        Sn UR                  [        R                  U/U	S	S	SS9nUR                  S:X  a5  U
" S	5      u  pÍnU(       a  [        U S35        XÍS	4$ UR                  5         OY[        R                  " UR                   R#                  5       =(       d    UR$                  R#                  5       5      =(       d    Un U(       a'  U
" S	5      u  pÍnU(       d  [        U SU S35        XÍU4$ U
" S5      u  pÍnSnU(       dv  US:  ap  [        SSU SU SSU-
   S35        TR)                  S5        US-  n T" U5      (       a  S	nO!SUR*                  ;  a  UR-                  TSSS9  U(       d  US:  a  Mp  XÍU4$ ! [&         a)  n[        R                  " U5      =(       d    Un SnANêSnAff = f! [&         a     NUf = f)aß  Open a site logged in, without needing anyone at the Mac (Steven
2026-09-02: "i want to be able to sync from any device... i dont want
to have to remotely access the host mac to login"). Tries the saved
session headless first; if it has expired, runs the site's own
auto_login script (the keychain credential) and tries again; only if
THAT fails does it fall back to a headed window and wait for a person,
saying why on the bar. ready(page) says whether the page is the logged
in one. Returns (ctx, page, logged_in); the caller owns ctx. quiet
(2026-09-07, the automatic stage check every few minutes): never a
window, a login that cannot be fixed by itself is reported as not
logged in and the caller leaves it for the next daily run.r   Nc                 óH   >•  [        T" U 5      5      $ ! [         a     gf = f©NF©rM   r¦   ©r°   Úreadys    €r0   Ú	_is_readyÚ"_open_logged_in.<locals>._is_ready¿  ó)   ø€ ð	Ü™˜d›Ó$Ð$øÜó 	Ùð	úó   ƒ ”
! !c                 ól  >• [         R                  " TTU U (       a  S O	[        5       S9nUR                  (       a  UR                  S   OUR	                  5       n UR                  TSSS9  [        S5       H'  nT" U5      (       a  XS4s  $ TR                  S5        M)     XS	4$ ! [         a     NFf = f)
Nr;  r   rˆ   rU  rŠ   râ  Tr  F)	r   r�   r9  rž   rŸ   r¡   r¦   r`  rC  )	r‡   r¯   r°   rj   rl  rb  r©   r†  rÂ  s	       €€€€€r0   Ú_tryÚ_open_logged_in.<locals>._tryÅ  s¦   ø€ Ü×)Ò)¨!¨T¸HÞ=E±TÌ?ÓK\ñ^ˆà"ŸyŸyˆs�y‰y˜Š|¨c¯l©l«nˆð	Ø�I‰I�cÐ&8À%ˆIÑHô �q–ˆAÙ˜�‰Ø $�Ò&Ø�H‰H�QŽKñ ð ˜%ÐÐøô ó 	Ùð	ús   ÁB& Â&
B3Â2B3c                  ó   • g rŸ  rG   )ro   rp   s     r0   rË   Ú!_open_logged_in.<locals>.<lambda>Ó  s   € ˜4rE   zdealerkit-loginzOpening z with the saved login...Tz
The saved zA session has expired, logging in again with the saved password...zthe saved login did not worké´   )r§  r¥  r¦  rŒ   z*: logged in again with the saved password.z: not logged in (z"); nothing read, no window opened.FrV  zCould not log into z by itself (z)). Waiting for you to log in on the Mac (r@  r  Úloginrˆ   rQ  rŠ   )rì   r©  r=   rí   r9  rª  r«  r™   r§   r¬  r­  r®  r²  r¨   r   rD  r¯  r  r°  r¦   rC  rÂ  r¡   )r©   r†  rÂ  rk  r³  rf   Úquietrì   r©  Úhererq  Úsayr¯   r°   r   Úreasonr   rK  rF  rl  rb  s   ````               @@r0   Ú_open_logged_inr{  ®  sG  ý€ ó ÛÛØ�7‰7�?‰?˜2Ÿ7™7Ÿ?™?¬8Ó4Ó5€Dõ÷ ñ  ö %*Ò¬y€CÙÐ˜X e WÐ,DÐEÔFÙ˜“J�M€CˆrÞ	Ø˜$ˆÐØ‡I�I„KÙÐ˜Z¨ wÐ.oÐpÔqØ+€Fð.Ø�N‰NœCŸN™N¨FÐ3¸ÈdÐY]ÐgjˆNÐkˆØ�<‰<˜1ÓÙ  ›J‰MˆC�rÞÜ˜˜ÐIÐJÔKØ $�Ð&Ø�I‰I�Kä—_’_ Q§X¡X§^¡^Ó%5×%I¸¿¹¿¹Ó9IÓJ×TÈf‰Fö Ù˜T›
‰ˆ�2ÞÜ�U�GÐ,¨V¨HÐ4VÐWÔXØ˜"ˆ}Ðá˜“K�M€CˆrØ€FÞ�V˜c“\ÜÐ#Ø'¨ w¨l¸6¸(ð C;Ø;>À¹<¸.ÈðQô	Rð 	�‰�ŒØ�!‰ˆð	Ù˜�‰Ø‘Ø §¡Ó(Ø—	‘	˜#Ð*<Àe�	ÑLö �V˜c•\ð �bˆ=Ðøô/ ó .Ü—’ Ó#×- v�ûð.ûô* ó 	Ùð	ús>   Â.AH+ ÄH+ ÄAH+ Ç'I! Ç7!I! È+
IÈ5IÉIÉ!
I.É-I.c           
      ó*   ^• [        U SUU4S jSSUS9$ )a  DealerKit through _open_logged_in: ready when the marker element is
on the page AND the API answers with real data (dealerkit_write
.session_alive, 2026-09-07: a page that renders while the API serves
the sign in page is not logged in, and used to pass here).r   c                 ód   >• U R                  T5      S L=(       a    [        R                  " U 5      $ rŸ  )r“  r	   Úsession_alive)r°   Úmarkers    €r0   rË   Ú!_open_dealerkit.<locals>.<lambda>	  s*   ø€ ¨×(;Ñ(;¸FÓ(CÈ4Ð(O×(wÔTc×TqÒTqÐrvÓTwÐ(wrE   zauto_login_dealerkit.pyÚ	DealerKit©rw  ©r{  )r©   rÂ  r  rw  s     ` r0   Ú_open_dealerkitr„  ÿ  s$   ø€ ô
 ˜1˜k¨3ÜwØ4°kÈñPð PrE   c                 ó$   • [        U SUS SS5      $ )z„LE Capital through _open_logged_in: ready off the login page with its own
"Displaying all" text, the site gives no simpler DOM hook.r   c                 óV   • SU R                   ;  =(       a    SU R                  S5      ;   $ )Nz/auth/loginzDisplaying allÚbody)rÂ  Ú
inner_text)r°   s    r0   rË   Ú!_open_lecapital.<locals>.<lambda>	  s(   € ¨¸T¿X¹XÑ(E×(uÐJZÐ^b×^mÑ^mÐntÓ^uÑJuÐ(urE   zauto_login_lecapital.pyr|  rƒ  )r©   rÂ  s     r0   Ú_open_lecapitalrŠ  		  s!   € ô ˜1˜k¨3ÙuØ4°lóDð DrE   c                  ó®  • SSK n SSKn[        R                  " 5         [	        S5      (       d  [
        R                  " S5      eU R                  R                  5       n[        R                  S   S    S3n[        S5        S	n0 / pe[        5        n[        XsS
5      u  p‰n [        R                  " SUU(       a  SOS5        U(       aU  U	R                  S5        [         R"                  " U	5      u  pVn
 [        R$                  " [         R&                  " Xš5      5         UR+                  [        R,                  " S5      S9  UR/                  5          SSS5        U(       d  [1        SS	S5        [        S5        gU(       d  [1        SS	S5        [        S5        g[3        SS[5        UR7                  5       5       S3SS5        [        R8                  " XbR;                  5       5        [=        5       u  pÍpï[?        XÍXàR                  RA                  U5      U5        [1        SS[5        UR7                  5       5       S3SS5        [        S[5        UR7                  5       5       S[C        U5       S35        g! [(         a  n[        SU S35         SnAGNxSnAff = f! [(         a     GNif = f! UR/                  5         f = f! , (       d  f       GNx= f)aL  On demand DealerKit stock refresh, the fallback for when the scheduled
read has been failing because the login expired. Opens a DealerKit window
on the Studio so Mark can log in if needed, reads the in stock list,
snapshots it, and rebuilds the cockpit so the gap and in stock flags are
current. Keeps the valuations, no re valuing.r   Nr"   zIDealerKit stock is turned off in Settings > Connections. Nothing changed.r   Úbase_urlzk/vehicles?instance.life_cycle_status=10&instance.market=%22retail%22&instance.asset_class=%5B%22stock%22%5DzDealerKit stock passFz.vehicle-titlerX   ú&The DealerKit login was not completed.éè  zDealerKit records not read (z)), the purchase costs keep the last read.rj  z,Stock not read, the login was not completed.z5DealerKit login/stock not completed. Nothing changed.z"Page read but no stock rows found.rZ  rž  rã  r[  r9   Tr\  zDealerKit stock refreshed: r   r]  )"r>   r=   r   rÎ   rc  r   rM  r<   rI   r   r]  r¨   r   r„  rE  r_  r   Úread_all_pages_with_idsrö  Úread_stock_recordsr¦   rD  rn  r§   rý  r™   rb  r÷  rõ  r@   r¶   r¹   r·   rš   )ry  rb  rI   rÂ  rG  r  r  r©   r¯   r°   ÚidsrK  rº   r»   r¼   rN   s                   r0   Údealerkit_passr’  	  sh  € ó ÛÜ‡J‚J„LÜÐ 1×2Ñ2Ü�oŠoÐiÓjÐjØ�H‰H�N‰NÓ€EÜ�m‰m˜KÑ(¨Ñ4Ð5ð 65ð 5€Cô 
Ð
 Ô!Ø€IØ˜ˆEÜ	Ô	˜aô  /¨qÐ7GÓHÑˆ�9ð	Ü×!Ò! +¨yÞ)2¡2Ð8`ôbæØ×%Ñ% dÔ+Ü%.×%FÒ%FÀtÓ%LÑ"�˜sðgÜ×+Ò+¬I×,HÒ,HÈÓ,SÔTðØ×!Ñ!¤w×'9Ò'9¸+Ó'FÐ!ÑGð �I‰I�K÷3 
ö6 ÜÐ! 5Ð*XÔYÜÐEÔFØÞÜÐ! 5Ð*NÔOÜÐ6Ô7ØÜˆg˜œs 6§=¡=£?Ó3Ð4Ð4PÐQÐSTÐVWÔXÜ×Ò˜U§O¡OÓ$5Ô6ä+6«=Ñ(€I�XÜ� (¯H©H×,BÑ,BÀ9Ó,MÈyÔYÜÐ˜t¬¨F¯M©M«OÓ(<Ð'=Ð=OÐ%PÐRSÐUVÔWÜ	Ð'¬¨F¯M©M«OÓ(<Ð'=¸[ÌÈVËÈÐU]Ð
^Õ_øô1 !ó gÜÐ8¸¸Ð;dÐe×fÒfûðgûô
 ó Úðûð �I‰I�Kú÷3 
Ö	úsg   ÂKÂ"AJ0Ã5*I8Ä #JÅKÉ8
JÊJÊJ0ÊJÊJ0Ê
J-Ê)J0Ê,J-Ê-J0Ê0KËKË
Kc                  ó¨  • SSK n SSKJnJnJn  [
        R                  " 5         [        S5      (       d  [        R                  " S5      e[
        R                  " 5       n[
        R                  " 5       R                  S5      =(       d    /  Vs1 s H+  oUR                  S5      (       d  M  UR                  S5      iM-     nn/ nU GH¨  n[
        R                  " U5      (       d  M!  / n	UR                  S	5      (       a(  UR                  S
5      (       d  U	R                  S5        OúUR                  S5      (       a'  UR                  S5      (       d  U	R                  S5        UR                  S5      b'  UR                  S5      (       d  U	R                  S5        UR                  S5      (       a'  UR                  S5      (       d  U	R                  S5        [
        R                  " U5      (       d  [
        R                   " U5      (       a  U	R                  S5        U	 HG  n
U R#                  XŠU5      nU(       a  [%        SUS    SU
 SU 35        M5  UR                  XŠ45        MI     GM«     ['        U5      n[)        U5      nU(       d"  U(       d  [+        SSSSS5        [%        S5        g[%        S[)        U5       S[)        U5       S 35        SnSn[-        5        n[/        U[0        R2                  S!   S"    S#3S$5      u  nnn [
        R4                  " S!UU(       a  S%OS&5        U(       d2  [+        SS'S(5        [%        S)5         UR7                  5         SSS5        g UR9                  [0        R:                  " S!5      S*9  U(       a  [?        [A        UXÆ5      5      nSn[C        S+S,U S-3SU5        U H�  u  nn
US-  n U
S:H  =(       a    [
        R                  " U5      (       + n[D        RF                  " UUU
US.9nU" UX¨5        U" UUU5        US-  n[%        S/US    SX:    SUS0    35        [C        S+S8U S9U SUS    SX:    3UU5        MŸ     UR7                  5          SSS5        U S9[)        U5       S:3nU(       a	  US;U S<3-  n[+        SSUU=(       d    SU=(       d    S5        [%        SU S=35        gs  snf ! [<         a     GNPf = f! [D        RH                   ah  n[
        R4                  " S!S'[K        U5      5        [+        SS'S1U 35        [%        S/US    SU 35         SnA  UR7                  5         SSS5        gSnAf[<         aF  n[
        RL                  " US2   S!S3X:    S4U 3S5S69  [%        S/US    SX:    S7U 35         SnAGNiSnAff = f! UR7                  5         f = f! , (       d  f       GNY= f)>aõ  The automatic DealerKit run (2026-08-22 for the Run modal; rebuilt
2026-09-05 for Dealer OS's redesigned DealerKit tab; narrowed the same
day to follow ups only). It NEVER sends a car to DealerKit whole:
since v3.14.3 a car goes to DealerKit only when a person presses Send
in Dealer OS (purchase.dk_send), Steven's decision, so a car with
everything on file but no Send click stays off DealerKit however many
runs pass.

What it does do, on cars already on DealerKit: a chip not yet sent, a
transport fee not yet sent, a check in not yet mirrored, a cancelled
sale not yet removed. Each is stamped on the row once done
(dk_chip_pushed_at, dk_transport_pushed_at, dk_checked_in_at,
dk_removed_at) so it is never repeated. The retail price left this
list on 2026-09-07: the stage read at the end carries it both ways
(retail_sync_plan), here and in the five minute stage check.

One continuous session for the whole batch, the same lesson glass_pass
learned the hard way: a fresh session per car reads as a logout.r   N)Úrecord_followup_outcomeÚrefresh_check_if_leftÚDK_ITEM_LABELSr#   úRDealerKit purchases push is turned off in Settings > Connections. Nothing changed.Ústock_statusÚfinishedrÏ  Úcancelled_atÚdk_removed_atr3  ÚchipsÚdk_chip_pushed_atÚchipÚtransport_feeÚdk_transport_pushed_atÚ	transportÚchecked_in_atÚdk_checked_in_atÚcheckinÚfigureszSKIPPED rW   r  rU  TzNothing new to sendr9   z!Nothing new to send to DealerKit.zDealerKit: z, follow up(s) on cars already on DealerKit, z stage(s) to readr   rŒ  ú	/vehiclesúinput[placeholder=Search]rX   r�  Fz%Not run, the login was not completed.z,DealerKit login not completed. Nothing sent.rj  rä  úSending z follow up(s) to DealerKit)Ú	fill_onlyr•   Úoutcomez	Stopped, Úidú
DealerKit z	 failed: ÚBidBrain©Úwhoz	 failed, zDone r”   z follow up(s) donerE  z matched to DealerKit's stagerG  )'ÚserveÚpurchases_runr”  r•  r–  r   rÎ   rc  r   rM  Úlist_purchasesÚget_purchase_dropdownsr+   Úon_dealerkitrH  Údk_figures_driftÚdk_figures_missingÚdk_followup_refusalr¨   Ú_stage_candidatesrš   rý  r   r„  r   r]  rE  r§   rD  rn  r¦   rb  Ú_dealerkit_stagesr™   r	   Úpush_followupÚDealerkitSessionExpiredrF  Úlog_purchase_event)Ú_server”  r•  r–  ÚrowsÚor™  Ú	followupsr   ÚwantedÚactionrJ  Úarrivalsr	  r®   Úmatchedr©   r¯   r°   rG  Ústepr‡  r©  rK  rÌ  s                            r0   Údealerkit_purchases_passrÆ  O	  se  € ó& ß\Ñ\Ü‡J‚J„LÜÐ 5×6Ñ6Ü�oŠoÐrÓsÐsä×ÒÓ€Dô *,×)BÒ)BÓ)D×)HÑ)HÈÓ)X×)^Ð\^Ð)^ÓuÒ)^ 1×chÑchÐis×ct“�—‘�g–Ñ)^€HÐuØ€IÜˆÜ�Š˜q×!Ñ!ÙØˆØ�5‰5�× Ñ Ø—5‘5˜×)Ñ)Ø—‘˜hÔ'øà�u‰u�W�~‰~ a§e¡eÐ,?×&@Ñ&@Ø—‘˜fÔ%Ø�u‰u�_Ó%Ñ1¸!¿%¹%Ð@X×:YÑ:YØ—‘˜kÔ*Ø�u‰u�_×%Ñ%¨a¯e©eÐ4F×.GÑ.GØ—‘˜iÔ(Ü×"Ò" 1×%Ñ%¬×)>Ò)>¸q×)AÑ)AØ—‘˜iÔ(ó ˆFð ×,Ñ,¨Q¸Ó=ˆCÞÜ˜  5¡ 
¨!¨F¨8°2°c°UÐ;Ö<à× Ñ  ! Ö-ô ñ% ô@ ! Ó&€Hä�	‹N€EÞžÜÐ% tÐ-BÀAÀqÔIÜÐ1Ô2Øä	ˆKœ˜I›Ð'Ð'SÔTWÐX`ÓTaÐSbÐbsÐ
tÔuØ€DØ€GÜ	Ô	˜aÜ.¨q´W·]±]À;Ñ5OÐPZÑ5[Ð4\Ð\eÐ2fð  iDó   EÑˆˆT�9ð&	Ü×!Ò! +¨yÞ)2¡2Ð8`ôbæÜÐ-¨uÐ6]Ô^ÜÐDÔEØð@ �I‰IŒK÷Q 
Ð	ðØ×!Ñ!¤w×'9Ò'9¸+Ó'FÐ!ÑGö ÜÔ/°°hÓIÓJ�ØˆDÜ�i 8¨E¨7Ð2LÐ!MÈqÐRWÔXÛ(‘��VØ˜‘	�ðSð !'¨)Ñ 3× T¼B×<OÒ<OÐPSÓ<TÔ8T�IÜ'×5Ò5°d¸CÀÐS\Ñ]�AÙ+¨C°Ô;Ù)¨$°°QÔ7Ø˜A‘I�DÜ˜B˜s 5™z˜l¨"¨^Ñ-CÐ,DÀAÀaÈ	ÁlÀ^ÐTÔUô ˜) u¨T¨F°$°u°g¸RÀÀEÁ
¸|È1È^ÑMcÐLdÐ%eÐgkÐmrÖsñ)  )ð, �I‰I�K÷Q 
ðT ��dœ3˜y›>Ð*Ð*<Ð=€GÞØ�R˜�yÐ =Ð>Ñ>ˆÜÐ! 4¨°%·*¸1¸e¿jÀqÔIÜ	ˆK˜�y Ð
"Õ#ùòw vøôp ó Úðûô$ '×>Ñ>ó Ü×)Ò)¨+°u¼cÀ!»fÔEÜÐ1°5¸IÀaÀS¸/ÔJÜ˜B˜s 5™z˜l¨"¨Q¨CÐ0Ô1Üð �I‰IŒK÷Q 
Ð	ûôD !ó SÜ×)Ò)¨#¨d©)°[Ø,6°~Ñ7MÐ6NÈiÐXYÐWZÐ*[Ðakòmä˜B˜s 5™z˜l¨"¨^Ñ-CÐ,DÀIÈaÈSÐQ×RÒRûðSûð �I‰I�Kú÷Q 
Ö	úsŸ   ÂS
Â+S
Ë$)WÌA V-ÍWÍ)#SÎ=V-Ï
A2S Ð<#V-ÑWÓ
SÓV-ÓSÓV-Ó V*Ó4AUÔ9V-Ô>WÕV*Õ$;V%ÖV-Ö%V*Ö*V-Ö-V?Ö?W×
Wc                 óœ   • U  Vs/ s H:  n[         R                  " U5      (       d  M   UR                  S5      (       a  M8  UPM<     sn$ s  snf )zÅThe live cars whose Status follows DealerKit: on DealerKit, not
cancelled. Arrived or not (an arrived car can still move to Ordered or
Sold, or back to In stock when a customer deal falls through).rš  )r   r´  r+   ©r¾  r   s     r0   r¸  r¸  Ê	  s4   € ñ ÓP’t�!œrŸš¨q×1‹A¸!¿%¹%À×:O�A‘tÑPÐPùÒPs   …A	¨A	Á A	c                 óZ  • S nU" U 5      U" U5      U" U5      p˜nS n
Uc  Uc  gU
" Xx5      (       a  SU4$ U(       a	  Ub  SU4$ S$ Uc  U(       a  U(       a  SU 4$ S$ Uc  SU4$ U	c  SU4$ U
" Xy5      (       + U
" X‰5      (       + pËU(       a  SU4$ U(       a  U(       a  U(       a  SU 4$ SU4$ g)aM  Which way a retail price moves, from the three figures the stage
check has for one car (pure, tested; 2026-09-07, Steven: "i just want
the prices to sync on both sides as instant as possible"). here is
Dealer OS's Retail est., there is DealerKit's Retail Price, agreed is
the last figure both sides were known to share (dk_check.retail_price).

Returns ("agree", x) when they already match and x is just to be
remembered; ("to_here", x) when DealerKit's figure should come into
Dealer OS; ("to_there", x) when Dealer OS's figure should go to
DealerKit; (None, None) when nothing can be done.

The side that changed since the two last agreed wins. When both have
changed, or nothing was ever agreed, DealerKit wins: it is the shop
floor's own screen and the car is worked on there once it has
arrived. One exception, the same one _retail_followup_wanted kept
before this: on a finished car (arrived, Sold, Ordered) Dealer OS's
figure only goes across when a person typed it there (by_hand); a
figure the formula re priced on its own never overwrites the team's
price on a car they are already selling.

locked (Steven, the same day, a hard rule): a car Ordered or Sold on
DealerKit has its price touched on DealerKit and nowhere else. Nothing
goes across from here, not even a figure a person typed; DealerKit's
figure still comes here so the row reads true.c                 óP   •  U c  S $ [        U 5      $ ! [        [        4 a     g f = frŸ  )Úfloatr5  rw  )rq   s    r0   ÚnumÚretail_sync_plan.<locals>.numê	  s1   € ð	Ø™9�4Ð2¬%°«(Ð2øÜœ:Ð&ó 	Ùð	ús   ‚ ‡
 ’%¤%c                 óN   • U S L=(       a    US L=(       a    [        X-
  5      S:  $ )Ng{®Gáz„?)Úabs)rû   r8  s     r0   rË   Ú"retail_sync_plan.<locals>.<lambda>ð	  s$   € ˜ ˜×M¨!°4¨-×M¼CÀÁ»JÈÑ<MÐMrE   rý   ÚagreeÚto_hereÚto_thererG   )rx  ÚthereÚagreedÚby_handr™  ÚlockedrÌ  ÚhrŽ  ro   ÚsameÚhere_changedÚthere_changeds                r0   Úretail_sync_planrÜ  Ñ	  sÔ   € ò2ñ
 �$‹i™˜U›¡S¨£[ˆ!€AÙM€DØ�y�Q‘YØÙˆA‡z�zØ˜ˆ~ÐÞØ%&¡]�	˜5Ð!ÐD¸ÐDØ�yÞ)1¶W�
˜DÐ!ÐNÀ,ÐNØ�yØ˜%ÐÐØ�yØ˜%ÐÐÙ&*¨1£j¤.±d¸1³j´.�-ÞØ˜%ÐÐÞÞ)1¶W�
˜DÐ!ÐTÀ9ÈeÐBTÐTØrE   c                 óÌ  • SnS=n=pVUcc  [         R                  " 5       R                  S5      =(       d    /  Vs1 s H+  owR                  S5      (       d  M  UR                  S5      iM-     nnU GHa  nUR                  S5      =(       d    0 R                  S5      n	U	(       dI  Uc  [        R                  " U 5      n[        R
                  " X8S   [        R                  " U5      5      u  pšU	(       d  M†   [        R                  " X	5      nUR                  S	5      [        R                  " U5      UR                  S
5      pín[        UR                  S5      =(       d    0 5      n[        R                   " UR                  S5      5      nUR                  S	5      UR                  S
5      4XÎ4:w  d-  UR                  S5      U:w  d  Ub_  UR                  S5      U:w  aJ  UR#                  XœXÞS.5        Ub  UR                  S5      US'   UUS'   [         R$                  " US   U5        [         R&                  " US   XÍUSS9nUS:X  a  US-  n[        SUS    SU S35        O US:X  a  US-  n[        SUS    SU S35        [        R(                  " U5      (       d  GM3  [+        UR                  S5      5      =(       d)    UR                  S5      =(       d    SU;   =(       d    US;   n[-        UR                  S5      UR                  S5      UR                  S5      UR                  S5      SLU[        R.                  " U5      S 9u  nnUS!:X  a:  UR                  S5      U:w  a"  UUS'   [         R$                  " US   U5        GM  GM   US":X  aˆ  [         R0                  " US   [3        [5        [7        U5      5      5      S#S$[7        U5      S% 3S&9  UUS'   [         R$                  " US   U5        US-  n[        SUS    S'[7        U5      S% S(35        GM®  US):X  d  GM·   [        R8                  " X	U5      nUUS'   [         R$                  " US   U5        [         R@                  " US   S./S/9  [         R:                  " US   S*S0[7        U5      S% S1US2:X  a  S3OS4 S53SS9  US-  n[        SUS    S'[7        U5      S% S6U S535        GMd     XEU4$ s  snf ! [        R                   a3  n[        R                  " X5      (       d  e [        X‰5         SnAGM±  SnAf[        R                   a    [        X‰5         GM×  [         a   n[        SUS    SU 35         SnAGMû  SnAff = f! [        R                   a    e [         aR  n[         R:                  " US   S*S+[<        R>                  " US,5       3SS9  [        SUS    S-U 35         SnAGMn  SnAff = f)7a—  DealerKit wins (2026-09-07): read each car's stage on DealerKit,
keep it on dk_check for Dealer OS, and move the car's Status here to
match (db.match_dealerkit_stage: In Stock checks it in with DealerKit's
arrival date, Awaiting Delivery is Ordered, Sold is Sold; Due In and
Non-Stock change nothing). The same read carries the retail price
both ways (retail_sync_plan). Returns (checked_in, moved, priced).Nr   r˜  r™  rÏ  Údk_checkÚ
vehicle_idrW   Úlife_cycle_statusÚdelivery_onr•   z: stage not read, r¢  ÚstageÚpurchase_cost_value)rß  rà  râ  rá  r«  r­  r®  r¤  r9   rU  z on DealerKit, checked in herer)   z  on DealerKit, Status moved herer¢  rX   )r¤  r)   Úretail_estimateÚretail_priceÚretail_estimate_override)rÖ  r™  r×  rÑ  rÒ  r�  u1   Retail est. followed DealerKit's Retail Price, Â£Úg)r¯  rz  u   : retail Â£z+ on DealerKit, Retail est. followed it hererÓ  r   z DealerKit: Retail price failed, rV  z: retail price not sent, Úretail)rm   u!   DealerKit: Retail price set to Â£rÀ   Úapizthrough the APIzon the Pricing panelrB  z sent to DealerKit ()!r   r³  r+   r	   Ú_stocklist_reg_mapÚvehicle_id_from_mapÚother_plateÚread_recordÚstage_labelr»  Úrecord_gone_not_sessionÚ_dealerkit_record_goneÚDealerkitRecordGoner¦   r¨   ÚdictÚpurchase_cost_figureÚupdateÚset_dealerkit_checkÚmatch_dealerkit_stageÚprice_syncablerM   rÜ  Úprice_lockedÚset_retail_estimate_overrider  r5  rË  Úset_retail_pricer¼  r   rD  Úmark_dealerkit_pushed)r°   r¾  r™  Úreg_mapÚ
checked_inÚmovedre  r¿  r   rß  rj   Úrecordrk   râ  Ú	deliveredrK  ÚdcÚcostÚwhatÚis_finishedÚwayÚfigureÚhows                          r0   r¹  r¹  
  s¹  € ð €GØ"#Ð#€JÐ#�ØÑÜ-/×-FÒ-FÓ-H×-LÑ-LÈ^Ó-\×-bÐ`bÐ-bÓyÒ-b q×glÑglÐmw×gx“N�A—E‘E˜'–NÑ-bˆÐyÜˆØ—e‘e˜JÓ'×-¨2×2Ñ2°<Ó@ˆ
ÞØ‰Ü)×<Ò<¸TÓB�ô ,×?Ò?Ø˜5™¤?×#>Ò#>¸qÓ#AóC‰MˆJæÙð	Ü$×0Ò0°ÓBˆFØ'-§z¡zÐ2EÓ'FÌ×HcÒHcÐdjÓHkÐms×mwÑmwð  yFó  nG˜9ˆFô �!—%‘%˜
Ó#×) rÓ*ˆÜ×3Ò3°F·J±J¸Ó4OÓPˆØ�F‰FÐ&Ó'¨¯©°Ó)>Ð?ÀFÐCVÓVÐZ\×Z`ÑZ`ÐahÓZiÐmrÓZrØÑ$¨¯©Ð0EÓ)FÈ$Ó)NØ�I‰I ZÐW\ÑwÔxØÑð '-§j¡j°Ó&A��?Ñ#Ø,0�Ð(Ñ)Ü×"Ò" 1 U¡8¨RÔ0Ü×'Ò'¨¨$©°À	ÈzÑZˆØ�9ÓØ˜!‰OˆJÜ�B�q˜‘x�j  5 'Ð)GÐHÕIØ�U‹]Ø�Q‰JˆEÜ�B�q˜‘x�j  5 'Ð)IÐJÔKô ×-Ò-¨f×5Ñ5ÚÜ˜1Ÿ5™5 Ó1Ó2×}°q·u±u¸^Ó7L×7RÐPRÐW_Ñ6_×}ÐcgÐk}Ñc}ˆÜ& q§u¡uÐ->Ó'?ÀÇÁÈNÓA[Ð]_×]cÑ]cÐdrÓ]sØ/0¯u©uÐ5OÓ/PÐX\Ð/\ÐgrÜ.=×.JÒ.JÈ6Ó.RñT‰ˆˆVð �'‹>Ø�v‰v�nÓ%¨Ó/Ø%+��>Ñ"Ü×&Ò& q¨¡x°×4ò 0ð �IÓÜ×+Ò+¨A¨d©G´S¼¼uÀV»}Ó9MÓ5NÐT_Ø5fÔglÐmsÓgtÐuvÐfwÐ3xòzà!'ˆBˆ~ÑÜ×"Ò" 1 U¡8¨RÔ0Ø�a‰KˆFÜ�B�q˜‘x�j ¬E°&«M¸!Ð+<Ð<gÐh×iØ�JÖðÜ%×6Ò6°tÈÓP�ð "(ˆBˆ~ÑÜ×"Ò" 1 U¡8¨RÔ0Ü×$Ò$ Q u¡X°h°ZÒ@Ü×!Ò! ! D¡'¨;Ø$EÄeÈFÃmÐTUÐEVÐVXÐnqÐuzÓnzÑYjð  AWð  YXð  XYð  #ZØ&0ò2ð �a‰KˆFÜ�B�q˜‘x�j ¬E°&«M¸!Ð+<Ð<PÐQTÐPUÐUVÐW×Xñ_ ð` ˜fÐ$Ð$ùòc zøô ×6Ñ6ó 	Ü"×:Ò:¸4×CÑCØÜ" 1Ô1ÞûÜ×2Ñ2ó 	Ü" 1Ô1ÛÜó 	Ü�B�q˜‘x�jÐ 2°1°#Ð6Ô7Þûð	ûôX #×:Ñ:ó ØÜó Ü×%Ò% a¨¡g¨{Ð>^Ô_c×_nÒ_nÐopÐruÓ_vÐ^wÐ<xð  Iò  JÜ˜˜1˜U™8˜*Ð$=¸a¸SÐAÔBÞûðúsO   ºSÁSÃ7ASÐ(U3ÓU0Ó3'T!Ô!"U0Õ	U0ÕU+Õ+U0Õ3W#ÖAW×W#c                 óž   • [         R                  " U S   S5        [         R                  " U S   SSU S3SS9  [        S	U S    S
U S35        g)zÄOne car whose DealerKit record no longer answers (2026-09-07): its
check is set to "no record" so the row reads not on DealerKit, a
timeline line says so, and the run carries on with the next car.rW   Nr«  r   z;DealerKit no longer answers for this car's record (vehicle z<); it reads as not on DealerKit until a Check finds it againr­  r®  r•   z: DealerKit record z. no longer answers, marked as not on DealerKit)r   rõ  r¼  r¨   )r   rß  s     r0   rð  rð  c
  sf   € ô ×Ò˜1˜U™8 TÔ*Ü×Ò˜!˜D™' ;ØWÐXbÐWcð  d`ð  aØ(ò*ô 
ˆBˆq�‰xˆjÐ+¨J¨<Ð7eÐ
fÕgrE   ).ú	/api/userú/api/mez/api/auth/userz/api/profilez/api/documentationz	/api/docsz/docsz/api/openapi.jsonz/openapi.jsonz	/api-docsz/api/tokensz/api/personal-access-tokensz/user/api-tokensz/settings/api-tokensz/settings/apiz/settings/integrationsz/api/integrationsz/api/settingsz/api/webhooksz/api/expensesz/api/expense-itemsz/api/purchasesz/api/vehiclesz
/api/leadsz/api/customersz/api/contacts?limit=1z/api/valuationsz/api/valuationz/api/dealersz/api/dealerz/api/locationsz
/api/usersz/api/documentsz
/api/mediaz/api/imagesú/api/stocklist?limit=1&offset=0z/api/funding?limit=1z/api/reservationsz
/api/salesz
/api/dealsz/api/invoicesz/api/reportsz/sanctum/csrf-cookiez/api/routesz
/telescopez/horizonc                 óØ   • SSK n/ nUR                  " SU 5       HL  nUR                  S5      R                  S5      nXB;  d  M*  [	        U5      S:”  d  M;  UR                  U5        MN     U$ )zåEvery /api/... path mentioned in a page or script, deduplicated, in
order of first appearance (pure, tested). The front end's own bundle
names every route its screens call, which is the main API's real
surface, documented or not.r   Nz ["'`](/api/[A-Za-z0-9_\-{}/.$]*)r9   ú/.rQ  )r›   Úfinditerr¥   Úrstriprš   rH  )r¦  r›   rg  r³   rí   s        r0   Úapi_paths_inr  {
  s^   € ó
 Ø€DØ�[Š[Ð@À$ÖGˆØ�w‰w�q‹z× Ñ  Ó&ˆØÕ¤ D£	¨A¥Ø�K‰K˜Öñ Hð €KrE   )Ú	api_tokenÚapiTokenz	api-tokenÚpersonal_accessÚpersonalAccessÚaccess_tokenÚsanctumúBearer ÚoauthÚpassportÚwebhookÚ
integratorzX-APIÚapiKeyÚapi_keyÚbaseURLÚwithCredentialsÚXSRFú/loginz/logoutÚ
two_factorÚ
impersonat)
z
/platform/z/js/z/css/z/img/z/images/z/fonts/z/svg/z/static/z/assets/z/node_modules/c                 ó  • SSK n/ nUR                  " SU 5       Hg  nUR                  S5      R                  S5      nUR	                  [
        5      (       d  XS;   a  MD  UR                  U5        [        U5      U:¼  d  Mf    U$    U$ )aŽ  Route like strings the front end's bundle builds its calls from,
with or without the /api/ prefix: a quoted path of two or more lower
case segments ("/stocklist/{id}", "/settings/users"), deduplicated in
order of first appearance, asset folders left out (pure, tested).
api_paths_in() only sees literal "/api/..." strings, and a bundle
that keeps its base URL in one place mentions the prefix once.r   Nz4["'`](/[a-z][a-z0-9\-]*(?:/[a-z0-9\-{}$:.]+)+)["'`?]r9   r  )r›   r  r¥   r  rn   Ú
ROUTE_SKIPrH  rš   )r¦  Úlimitr›   rg  r³   rí   s         r0   Úroute_words_inr'  ’
  sy   € ó Ø€DØ�[Š[ÐTÐVZÖ[ˆØ�w‰w�q‹z× Ñ  Ó&ˆØ�?‰?œ:×&Ñ&¨$«,ÙØ�‰�DÔÜˆt‹9˜ÕØØ€Kñ \ð €KrE   éF   rÿ   c           	      ó¸  • SSK n/ nU HÈ  nUR                  " UR                  " U5      U 5       Vs/ s H  owR                  5       PM     nnU(       d  ML  / n	USU  HT  n
U [	        SX¢-
  5      U
[        U5      -   U-    nU	R                  UR                  " SSU5      R                  5       5        MV     UR                  U[        U5      U	45        MÊ     U$ s  snf )a  (word, count, [snippets]) for every word that appears in a script,
a few short windows of context each, whitespace collapsed (pure,
tested). How a front end signs its API calls (a cookie, a bearer, a
token it was handed at login) shows up in exactly these words.r   Nú\s+r  )	r›   r  ÚescapeÚstartrð   rš   rH  Úsubr  )r¦  ÚwordsÚaroundÚperr›   r,  rt   r³   ÚidxÚsnipsr±   Úss               r0   Úkeyword_hitsr4  ¥
  sÈ   € ó
 Ø
€CÛˆØ"$§+¢+¨b¯iªi¸«l¸DÔ"AÓBÒ"A˜Q�w‰wŽyÑ"AˆÐBÞÙØˆØ�T�c“ˆAØ”S˜˜A™JÓ'¨¬S°«V©°fÑ)<Ð=ˆAØ�L‰L˜Ÿš ¨¨QÓ/×5Ñ5Ó7Ö8ñ ð 	�
‰
�A”s˜3“x Ð'Ö(ñ ð €Jùò Cs   ±C)ÚemailÚphoneÚmobileÚ	telephoneÚaddressÚpostcodeÚ
first_nameÚ	last_nameÚsurnameÚforenameÚdobÚdate_of_birthÚnationalÚlicenceÚlicenser  ÚbankÚibanÚ	sort_codeÚaccount_numberc                 óä  ^• / n[        U =(       d    0 R                  5       5      SU  GHB  u  p4[        U5      R                  5       m[	        U5      R
                  n[        U[        [         45      (       a+  UR                  [        U5      U[        U5       S345        Mz  [        U4S j[         5       5      (       a  UR                  [        U5      US45        M·  Ub   [        U[        [        [        45      (       a)  UR                  [        U5      U[        U5      45        GM  [        U5      nUR                  [        U5      U[        U5      S::  a  UOUSS S-   45        GME     U$ )aY  (name, type, value) for every field on one record, values kept only
where they cannot be someone's personal details: numbers, true, false,
nothing, and short text on a field whose name is not one of the
personal ones (pure, tested). A field name alone is never personal, so
names are always shown; this is what a log sent to Dealer OS may carry.Nz insidec              3   ó,   >#   • U  H	  oT;   v •  M     g 7frŸ  rG   )rW  rt   Úlows     €r0   rX  Úsafe_fields.<locals>.<genexpr>É
  s   øé € Ð2¢>˜a�c–¢>ùs   ƒr…  rT  ú...)rB  rm   rF  r  ÚtypeÚ__name__Ú
isinstancerò  rH  rš   r½  ÚPERSONAL_WORDSr  rË  rM   )r‡  r&  r,  rp   rq   Úkindr3  rJ  s          @r0   Úsafe_fieldsrR  ½
  s  ø€ ð €CÜ�c—i˜R×&Ñ&Ó(Ó)¨&¨5Ô1‰ˆÜ�!‹f�l‰l‹nˆÜ�A‹w×ÑˆÜ�aœ$¤˜×&Ñ&Ø�J‰Jœ˜A› ¬¨Q«¨°Ð&8Ð9Ö:ÜÔ2¥>Ó2×2Ñ2Ø�J‰Jœ˜A›  hÐ/Ö0Ø‰Yœ* Q¬¬e´TÐ(:×;Ñ;Ø�J‰Jœ˜A› ¤c¨!£fÐ-×.ä�A“ˆAØ�J‰Jœ˜A› ¬3¨q«6°R«<¡a¸Q¸sÀ¸VÀe¹^ÐL×Mñ 2ð €JrE   c                 ó’   • SS K nUR                  " SSU =(       d    S5      nUR                  " SSU5      R                  5       nUS U $ )Nr   z<[^>]+>r  rX   r*  )r›   r-  r  )r¦  r&  r›   rŽ  s       r0   Ú_probe_snippetrT  Ó
  sB   € ÛØ
�Šˆz˜3 §
¨Ó+€AØ
�Šˆv�s˜AÓ×$Ñ$Ó&€AØˆVˆeˆ9ÐrE   c                  óÒ  ^• SSK n [        R                  " 5         [        R                  R                  [        R                  R                  [        R                  R                  [        5      5      SSS5      n[        R                  " [        R                  R                  U5      SS9  / mU4S jnU" S	[        R                  R                  5       R                  S
S9 35        [        5        n [        R                  " USSS9n UR                   (       a  UR                   S   OUR#                  5       nUR%                  [        R&                  S   S   SSS9  UR)                  SSS9  UR+                  S5      nU" S[-        U5       S35        USS  H  nU" S[/        U5      SS -   5        M     UR1                  5         Sn	S H~  u  p«[9        SSU S 35         [:        R<                  " USU
S!9n[?        S5        Sn	U" S"U S#[-        U5       S$35        [A        S% U 5       5      nU" S"U S&U S'[-        U5       S(35          O   U	(       d  U" S,5        [9        SS-5        [        R                  " USSS9n UR                   (       a  UR                   S   OUR#                  5       nUR%                  [        R&                  S   S   SSS9  UR)                  SSS9  URC                  S.5        [:        RD                  " URG                  5       5      nU" S/[-        U5       35         U" S0[/        UR+                  S15      5      SS -   5        USS2  GHM  n[:        RH                  " UU5        U" S3URJ                  =(       d    URL                   S4URN                  < S5URP                  < 35         URS                  S65      nURU                  S75       Vs/ s H:  nU RW                  S8UU RX                  5      (       d  M'  UR[                  5       SS9 PM<     nnU" S:U(       a  S;R                  USS< 5      OS=-   5        URU                  S75       Vs/ s H  nS>U;   d  M  UR[                  5       SS? PM      nnU" S@U(       a  S;R                  USSA 5      OS=-   5        GMP     UR1                  5          SSS5        []        USDSESF9 nUR_                  S7R                  T5      S7-   5        SSS5        [a        SGSSH[-        T5       SI3SJSJ5        g! UR1                  5         f = f! [2         a+  nU" S[4        R6                  " US5       35         SnAGN–SnAff = f! [2         a0  nU" S"U S)[4        R6                  " US*5       S+35         SnAGMÏ  SnAff = f! [2         a     GN[f = fs  snf s  snf ! [2         a+  nU" SB[4        R6                  " U5       35         SnAGM”  SnAff = f! [2         a+  nU" SC[4        R6                  " US*5       35         SnAGNzSnAff = f! UR1                  5         f = f! , (       d  f       GN�= f! , (       d  f       GNh= f)Kai  A one off look at Motorway without a run (Steven 2026-09-15: "can we
not just check it now as a one off without performing or waiting for a
run"). Presses Download once and writes what Motorway's export calls
answered (motorway.read_export writes motorway_export.log when no file
comes; a file that does come clears the failed marker so tonight's run
uses the CSV again), then opens the first car on the list and writes
every line of its page naming CAP. Reads only: no valuations, no
shortlist, no push, the daily list untouched. Everything lands in
data/logs/motorway_probe.log for Dealer OS's diagnostics to carry.r   NrU   r+  zmotorway_probe.logTr6  c                 ó>   >• TR                  U 5        [        U 5        g rŸ  ©rH  r¨   ©r  r,  s    €r0   ry  Ú motorway_probe_pass.<locals>.sayé
  ó   ø€ Ø�
‰
�4Ôœ% �+rE   zMotorway one off check rå   ræ   r   r†   rN  rˆ   rO  rŠ   rP  rQ  rR  zV() => Object.entries(localStorage).map(([k, v]) => k + ' = ' + String(v).slice(0, 70))z0stored site data on the Mac's Motorway profile: z entriesrT  r•   é    zstored site data not read: F))Fzas the run does)Tz#after clearing the stored site datarî  zPressing Download on Motorway rL  )r‡   Úclear_storagezCSV export z
: worked, z cars in the file.c              3   óJ   #   • U  H  oR                   (       d  M  S v •  M     g7fr”  )Ú	cap_cleanrÚ  s     r0   rX  Ú&motorway_probe_pass.<locals>.<genexpr>  s   é € Ð>ª$ Q·+µ+Ÿq™qª$ùrÛ  rU  r”   z rows carry a CAP clean valuez
: failed (r?  z4). What Motorway answered is in motorway_export.log.zCSV export: no file either way.z%Opening one car's page on Motorway...rS  zcards on the page: zbrowser says it is: z() => navigator.userAgentr  zcar z
: reserve z, cap clean read r‡  rÇ   z\bcap\béŒ   z  page lines naming CAP: z || é   r1  ô   Â£éd   u     page lines with a Â£ figure: r¿   z  page words not read: zcar page read failed: rt   ru   rv   Úmotorway_probezCheck written, z& lines in data/logs/motorway_probe.logr9   )1r›   r   rÎ   rì   rí   rI  r9  rª  r«  r8  r>   rA   r@   r   r   r�   rž   rŸ   r¡   r]  r¢   rc  rš   rF  r§   r¦   r   rD  r™   r   rC  r:  rb  r_  rd  re  rW  rW   r˜   r_   r^  rˆ  r  r¤   ÚIr  ry   rÂ   rý  )Ú_reÚlog_pathry  r©   Úctx0rH  Úkeysrp   rK  ÚworkedÚclearr  r¾  Úwith_capr¯   r°   rû  rÝ   r‡  ÚlÚhitsÚmoneyr}   r,  s                          @r0   Úmotorway_probe_passrp  Ú
  s�  ø€ ó Ü‡J‚J„LÜ�w‰w�|‰|œBŸG™GŸO™O¬B¯G©G¯O©O¼HÓ,EÓFÈÐPVÐXlÓm€HÜ‡K‚K”—‘—‘ Ó)°DÒ9Ø
€Cõ&áÐ
!¤(×"3Ñ"3×"7Ñ"7Ó"9×"CÑ"CÈYÐ"CÐ"WÐ!XÐYÔZÜ	Ô	˜að
	IÜ×.Ò.¨q°*ÀtÑLˆDð	Ø&*§j§j�T—Z‘Z ’]°d·m±m³o�Ø—‘œŸ™ jÑ1°+Ñ>ÐK]Ðgl�ÑmØ×$Ñ$Ð%<ÀeÐ$ÑLØ—{‘{Ð#{Ó|�ÙÐFÄsÈ4ÃyÀkÐQYÐZÔ[Ø˜c˜r›�AÙ˜œs 1›v d s˜|Ñ+Ö,ñ #ð —
‘
”ð ˆÛe‰JˆEÜ�iÐ#AÀ#ÀÀcÐ!JÔKð	@Ü×+Ò+¨A¸ÈEÑR�Ü  Ô&Ø�Ù�k #  j´°T³°Ð;MÐNÔOÜÑ>©$Ó>Ó>�Ù�k #  b¨¨
°$´s¸4³y°kÐA^Ð_Ô`Ùñ fö ÙÐ1Ô2Ü�)ÐDÔEÜ×)Ò)¨!¨ZÀ$ÑGˆð	Ø#&§9§9�3—9‘9˜Q’<°#·,±,³.ˆDØ�I‰I”g—m‘m JÑ/°Ñ<ÐI[ÐejˆIÑkØ×"Ñ"Ð#:ÀEÐ"ÑJØ×!Ñ! $Ô'Ü×)Ò)¨$¯,©,«.Ó9ˆDÙÐ%¤c¨$£i [Ð1Ô2ðÙÐ*¬S°·±Ð?ZÓ1[Ó-\Ð]aÐ^aÐ-bÑbÔcð ˜"˜1”X�Ü×+Ò+¨D°!Ô4Ù�d˜1Ÿ5™5×1 A§M¡MÐ2°*¸Q¿Y¹Y¹MÐIZÐ[\×[fÑ[fÑZiÐjÔkðHØŸ?™?¨6Ó2�DØ59·Z±ZÀÔ5EÓjÒ5E°ÈÏÉÐT^Ð`aÐcf×chÑch×Ii›O˜AŸG™G›I d s›OÑ5E�DÐjÙÐ3ÖQU°v·{±{À4ÈÈÀ9Ô7MÐ[aÑbÔcØ6:·j±jÀÔ6FÓTÒ6F°È$ÐRSÉ)›_˜QŸW™W›Y t¨›_Ñ6F�EÐTÙÐ9ÖX]¸V¿[¹[ÈÈsÐPRÈÔ=TÐciÑj×kñ ð �I‰I�K÷{ 
ô| 
ˆh˜ gÒ	.°!Ø	�‰�—	‘	˜#“ Ñ%Ô&÷ 
/äÐ˜d o´c¸#³h°ZÐ?eÐ$fÐhiÐklÕmøða —
‘
•ûÜó 	IÙÐ-¬d¯oªo¸aÀÓ.EÐ-FÐG×HÒHûð	Iûô ó @Ù�k #  j´·²ÀÀCÓ1HÐ0IÐI}Ð~×Óûð@ûô ó Úðüò kùâTøä ó HÙÐ1´$·/²/À!Ó2DÐ1EÐF×GÓGûðHûäó 	DÙÐ(¬¯ª¸¸CÓ)@Ð(AÐB×CÒCûð	Dûð �I‰I�Kú÷{ 
Ö	ú÷| 
/Ö	.ús6  Ã3YÃ5T0ÄB.TÆ:T0Ç
YÇ$A(U(É5YÊB.W8Ì1&V%ÍA$W8Î<$W Ï &V6Ð
V6Ð!;W Ñ
V;Ñ*V;Ò(W Ò)W8Ò.YÓ$YÔT-Ô-T0Ô0
U%Ô: U ÕYÕ U%Õ%YÕ(
V"Õ2$VÖYÖV"Ö"YÖ%
V3Ö/W8Ö2V3Ö3W8Ö6
W × 
W5×
W0×)W8×0W5×5W8×8
X-Ø X(Ø"X0Ø(X-Ø-X0Ø0YÙYÙ
YÙ
Y&c                  ó   ^3^4^5^6^7^8• SSK m3SSKn SSKn[        R                  " 5         [
        R                  =(       d    SR                  S5      m4[        [
        SS5      =(       d    SnT4(       d  [        R                  " S5      e[        R                  R                  [        R                  R                  [        R                  R                  [         5      5      SSS	5      n[        R"                  " [        R                  R                  U5      S
S9  / m5U54S jm8T8" S[$        R$                  R'                  5       R)                  SS9 ST4 SU(       a  SOS 35        [+        5        nUR,                  R/                  S
S9nUR1                  5       nUR2                  m6S¬U6U84S jjnU(       ag  SU 3SS.nT8" S5        U" SSU5        U" SSU5        T8" S5        S H  n	U" S U	 3T4U	-   U5        M     T8" S!5        S" H  n	U" S U	 3T4U	-   U5        M     OT8" S#5        T8" S$5        U" S%T4S&-   S'S05        UR5                  5         T8" S(5        [6        R8                  " US)S
S9n
U
R:                  (       a  U
R:                  S   OU
R=                  5       n[>        R@                  " U5      nT8" S*U 35        U
R2                  m7[B         HÓ  n	 T7RE                  T4U	-   S'S0S+SS,9nURG                  5       nURI                  5       RK                  S-5      (       a  S.OS/USS0 RM                  5       ;   a  S1OS2nURN                  RE                  S3S5      nT8" S4U	 S5URP                   S6U S6[S        U5       S7U(       a  S8U-   OS S4US.:X  a  [U        US95      OS 35        MÕ     T8" S;5         UR[                  T4S<-   S=S>S?9  UR]                  S@5        UR_                  5       nU R`                  " SAU5      nT8" SB[S        U5       35        [c        U5      nUSSC  H°  nURK                  SD5      (       a  UOT4URK                  S5      (       a  UOSU-   -   n T7RE                  USESF9nURG                  5       n[c        U5      nT8" S4USGS  SH[S        U5       SI[S        U5       SJ35        U H  nUU;  d  M  URe                  U5        M     M²     T8" S4[S        U5       SK35        USSL  H  nT8" SMU 35        M     T8" SN5        / 0 nnUSSC  HÓ  nS)U;  a  M  URK                  SD5      (       a  UOT4URK                  S5      (       a  UOSU-   -   n T7RE                  USESF9RG                  5       n[g        U5      nT8" S4USGS  SH[S        U5       SO35        U H  nUU;  d  M  URe                  U5        M     [i        U5       H  u  nnnUU;  d  M  UUUSPS 4UU'   M     MÕ     T8" S4[S        U5       SQ35        USS0  H  nT8" SMU 35        M     T8" SR[S        U5       SS35        URk                  5        H1  u  nu  nnn T8" SMU< SHU STU  35        U H  n!T8" SUU! SV35        M     M3     T8" SX5         URm                  SY5      n"URm                  SZ5      n#T8" S[S\R                  U"RE                  S]5      =(       d    / 5       35        T8" S^U"RE                  S_5       S`U"RE                  Sa5       Sb35        T8" U#(       a3  ScURo                  U#Rq                  5       5      Rs                  5       SSd  3OSe5        U#(       aû  UR1                  5       n$SfU#SSg.4ShU#SSiSj.4SkSU# 3SS.44 H¿  u  n%n&Sl H³  n	 U$R2                  RE                  T4U	-   U&S+SS,9nURG                  5       nURI                  5       RK                  S-5      (       a  S.OS/USS0 RM                  5       ;   a  S1OS2nT8" S4U% S6U	 S5URP                   S6U S6[S        U5       SmUS.:X  a  [U        USn5      OS 35        Mµ     MÁ     U$R5                  5         U
R5                  5         T8" Sp5         Sn' T7RE                  T4S&-   S+SF9R                  5       n(U(=(       d    0 RE                  Sq5      =(       d    / n)U)(       a  U)S   =(       d    0 RE                  Sr5      OSn'T8" SsU' 35        S­U3U4U7U84St jjn*U*" SuSv5      n+[u        U+[v        5      (       aq  Sw Hk  n,U+RE                  U,5      n-[u        U-[x        5      (       d  M+  U-(       d  M4  T8" SxU,< Sy35        [{        U-S   5       H  u  n.nn/T8" SzU. S{U S|U/ 35        M       O   U*" S}S~5        U*" SS€5        U'(       a1  S�S‚U' Sƒ34S„S‚U' S…34S†S‡U' Sˆ34S‰SŠU' S‹344 H  u  n%n	U*" U%U	5        M     U*" SŒS�5        U*" SŽS�5        T8" S‘5         UR1                  5       n0U0R2                  RE                  T4S’-   SESF9nURG                  5       nU R`                  " S“UU R|                  S”9n1T8" S•URP                   S\[S        U5       SI[S        U15       S–35        U1SS—  H  n2T8" SMU2SS˜  35        M     U R`                  " S™UU R|                  S”9SSš  H  n.T8" S›U. 35        M     [c        U5      SSœ  H  nT8" S�U 35        M     [g        USž5       H  nT8" SŸU 35        M     [i        US S¡9 H  u  nnnT8" SMU< SHU S¢US    SV35        M     U0R5                  5         UR5                  5         SSS5        [        US¤S¥S¦9 n2U2R�                  S§R                  T55      S§-   5        SSS5        [ƒ        S¨S
S©[S        T55       Sª3S«S«5        g! [V         a.  nT8" S4U	 S:[        RX                  " U5       35         SnAGMâ  SnAff = f! [V         a1  nT8" S4USGS  S:[        RX                  " U5       35         SnAGMÔ  SnAff = f! [V         a1  nT8" S4USGS  S:[        RX                  " U5       35         SnAGM!  SnAff = f! [V         a*  nT8" SW[        RX                  " U5       35         SnAGNûSnAff = f! [V         a1  nT8" S4U% S6U	 S:[        RX                  " U5       35         SnAGM6  SnAff = f! [V         a*  nT8" So[        RX                  " U5       35         SnAGN¢SnAff = f! [V         a     GN1f = f! [V         a*  nT8" S�[        RX                  " U5       35         SnAGNcSnAff = f! [V         a*  nT8" S£[        RX                  " U5       35         SnAGNFSnAff = f! , (       d  f       GNE= f! , (       d  f       GN = f)®a  What DealerKit's two APIs actually allow from this Mac, written to
data/logs/dealerkit_probe.log for Dealer OS's diagnostics to carry
(2026-09-07). Three parts: the Integrator API with the key in
dealer_config (DEALERKIT_API_SECRET); the tenant's own main API with
that key as a bearer and no cookies (does the key open the user space
API?); and, with the saved login, a sweep of candidate routes plus
every /api/ path the front end's own scripts mention. Reads only, no
secrets printed, nothing changed on DealerKit.r   NrX   r–   ÚDEALERKIT_API_SECRETz<DealerKit is not set up on this Mac (no DEALERKIT_BASE_URL).rU   r+  zdealerkit_probe.logTr6  c                 ó>   >• TR                  U 5        [        U 5        g rŸ  rW  rX  s    €r0   ry  Ú!dealerkit_probe_pass.<locals>.sayC  rZ  rE   zDealerKit API probe rå   ræ   z	  tenant z  key r)   únot setr†   c                 ó6  >•  TR                  X=(       d    0 SSS9nUR                  5       nUR                  5       R                  S5      (       a  SOSUS S R	                  5       ;   a  SOS	nUR
                  R                  S
S5      nT	" SU  SUR                   SU S[        U5       SU(       a  SU-   OS SUS:w  a  [        U5      OS 35        UR                  U4$ ! [         a,  nT	" SU  S[        R                  " U5       35         S nAgS nAff = f)Nr>  r   ©ÚheadersrŒ   Úmax_redirects©Ú{Ú[r{   ú<htmlé�  rc  r¦  ÚlocationrX   r•   ú: HTTP r  Úbú -> ú
: failed, )NrX   )r+   r¦  r  rn   r  rx  rk   rš   rT  r¦   r   rD  )
rf   rÂ  rx  r   r‡  rQ  ÚlocrK  Úreqry  s
           €€r0   ÚprobeÚ#dealerkit_probe_pass.<locals>.probeK  s*  ø€ ð	 Ø—G‘G˜C¯°BÀÐUV�GÐW�Ø—v‘v“x�Ø!%§¡£×!8Ñ!8¸×!DÑ!D‘vÐT[Ð_cÐdhÐehÐ_i×_oÑ_oÓ_qÓTqÉ&Ðw}�Ø—i‘i—m‘m J°Ó3�Ù�b˜˜˜w q§x¡x j°°$°°q¼¸T»¸À1ÖWZÀfÈsÂlÐ`bÐDcÐceð  Cð  GMó  MÔftÐuyÔfzð  SUð  fVð  Wô  XØ—x‘x �~Ð%øÜó  Ù�b˜˜˜z¬$¯/ª/¸!Ó*<Ð)=Ð>Ô?Üûð ús   ƒCC" Ã"
DÃ,"DÄDr  úapplication/json)ÚAuthorizationÚAcceptz-A. Integrator API with the key, from this Macz(GET api.dealerkit.uk/integrators/dealersz,https://api.dealerkit.uk/integrators/dealersz)GET api.dealerkit.uk/integrators/webhooksz-https://api.dealerkit.uk/integrators/webhooksz=B. The tenant's main API with the key as a bearer, no cookies)r  r	  r
  zGET z8B2. The Integrator paths on the tenant host with the key)z/integrators/dealersz/integrators/stock?dealer_id=1z8A/B skipped: no DEALERKIT_API_SECRET in dealer_config.pyzJC. The tenant's main API with no key and no cookies (what a stranger sees)z#GET /api/stocklist?limit=1&offset=0r  rŠ  z)D. With the saved login: candidate routesr   z!  saved login alive for the API: r>  rw  rz  r{   r}  r~  rc  r¦  r  r•   r€  r  r�  r‚  r[  rƒ  z7E. Every /api/ path the front end's own scripts mentionr¦  rˆ   rU  rŠ   iÄ	  z <script[^>]+src=["']([^"']+)["']z  page scripts: é   ÚhttprQ  rR  iºÿÿÿrU  zb, z
 api pathsz distinct /api/ paths:rV  z    z@F. Route strings and sign in words in the front end's own bundlez route stringsiØÿÿÿz distinct route strings:z  sign in words (z):z in z
      ... z ...z  front end read failed, z2H. The page's own API token, tried with no cookiesa  () => {
              const ui = (window.marque && window.marque.ui) || {};
              const t = ui.apiToken || "";
              return { keys: Object.keys(ui).sort(), tokenLength: t.length, tokenShape: t.replace(/[A-Za-z]/g, "a").replace(/[0-9]/g, "9").slice(0, 12) };
            }zL() => (window.marque && window.marque.ui && window.marque.ui.apiToken) || ''z  marque.ui keys: rE  ri  z  apiToken: ÚtokenLengthz characters, shape Ú
tokenShaperL  z  fingerprint: é
   z  no token on the pagezX-API-TOKEN only)úX-API-TOKENrŠ  zX-API-TOKEN + X-Requested-WithÚXMLHttpRequest)r�  rŠ  zX-Requested-WithzAuthorization Bearer)r  ú/api/deals?limit=1z/api/vehicles?limit=1zb  éP   z  token read failed, z I. What the unused routes returnÚresultsrß  z&  a vehicle id to try things against: c                 ó–  >•  TR                  TU-   SS0SSS9nUR                  5       nUR                  S:w  d$  UR                  5       R	                  S5      (       d5  T" SU  S	U S
UR                   S	[        U5       S[        US5       3
5        g TR                  U5      n[        U[        5      (       aP  SU;   aJ  T" SU  S	U SUR                  S5       S35        UR                  S5      =(       d    / nU(       a  US   OS nOP[        U[        5      (       a*  T" SU  S	U S[        U5       S35        U(       a  US   OS nOT" SU  S	U S35        UnU(       a-  U(       a&  [        U5       H  u  p‰n
T" SU SU	 SU
 35        M     U$ ! [         a/  nT" SU  S	U S[        R                  " U5       35         S nAg S nAff = f)NrŠ  rˆ  i¨a  r   rw  r?  rz  r•   r  r€  zb rá  r”  z: HTTP 200, r	  z	 in totalz rowsz: HTTP 200, one recordú      rÀ   ú) = rƒ  )r+   r¦  rk   r  rn   rš   rT  ÚloadsrO  rò  rB  rR  r¦   r   rD  )rf   rí   Úshowr   r‡  rU   r¾  r‡  rú   rQ  rO  rK  Ú_jsonr.   Úreq2ry  s               €€€€r0   ÚshapeÚ#dealerkit_probe_pass.<locals>.shapeâ  sÄ  ø€ ð ØŸ™ ¨¡°xÐASÐ6TÐ^cÐst˜Ðu�AØŸ6™6›8�DØ—x‘x 3“¨d¯j©j«l×.EÑ.EÀj×.QÑ.QÙ˜b   q¨¨¨g°a·h±h°Z¸qÄÀTÃÀÈ2ÌnÐ]aÐceÓNfÐMgÐhÔiØ#Ø Ÿ;™; tÓ,�DÜ! $¬×-Ñ-°)¸tÓ2CÙ˜b   q¨¨¨l¸4¿8¹8ÀGÓ;LÐ:MÈYÐWÔXØ#Ÿx™x¨	Ó2×8°b˜Þ)-˜d 1šg°4™Ü# D¬$×/Ñ/Ù˜b   q¨¨¨l¼3¸t»9¸+ÀUÐKÔLÞ)-˜d 1šg°4™á˜b   q¨¨Ð.DÐEÔFØ"˜ÞžtÜ/:¸3Ö/?™O˜D¨Ù &¨¨¨b°°°d¸3¸%Ð @ÖAñ 0@à�JøÜ ó  Ù˜"˜U˜G 1 T F¨*´T·_²_ÀQÓ5GÐ4HÐIÔJÜûð ús   ƒBF ÂC;F Æ
GÆ%GÇGÚcostsz/api/expenses?limit=1)rm   Úexpense_itemsÚlinesz    one cost line under Ú:r–  rÀ   r—  Údealsr’  Úinvoicesz/api/invoices?limit=1zvaluation nowz/api/vehicles/z/current-valuationÚ
valuationsz/valuationszpricing insightsz/api/stock-list/z/pricing-insightszprep summaryz /api/preparation/stock-listings/z/stage-summaryzprep across the yardz/api/preparation/stage-summaryz
stock feedz/api/stock-list/feed?limit=1z  route shapes failed, z#G. The sign in page with no cookiesr!  z<form[^>]*>)rl   z  GET /login: HTTP z form(s)rQ  r?  z <input[^>]+name=["']([^"']+)["']ra  z
    input r7   z    api path rT  z
    route r  )r0  z  ... z  sign in page read failed, rt   ru   rv   rÇ   Údealerkit_probezProbe written, z' lines in data/logs/dealerkit_probe.logr9   rŸ  )T)Br{   r›   Úhashlibr   rÎ   rº  ÚDEALERKIT_BASE_URLr  rZ   r   rM  rì   rí   rI  r9  rª  r«  r8  r>   rA   r@   r   ÚchromiumÚlaunchÚnew_contextÚrequestr§   r   r�   rž   rŸ   r	   r~  ÚPROBE_ROUTESr+   r¦  r  rn   r  rx  rk   rš   rT  r¦   rD  r¡   r_  re  r*  r  rH  r'  r4  rm   rc  Úsha256ÚencodeÚ	hexdigestrO  rò  rB  rR  re  ry   rÂ   rý  )9r›   r¦  Úsecretrg  r©   r�  r¯   r†  ÚHrí   Úctx2r°   Úliver   r‡  rQ  r„  rK  rc  ÚscriptsÚfoundr  rÂ  ÚtxtÚpathsrû   Úroutes_foundÚhits_allÚrwrt   r  r2  Úwherer3  ÚinfoÚtokenÚctxhrf   rx  Úfirst_idÚslr¾  rœ  Úexpr/   Úkidsrú   rO  Úctx3Úformsr}   rš  r.   r,  r…  r›  ry  s9                                                      @@@@@@r0   Údealerkit_probe_passrÅ  /  sÈ  ý€ ó ÛÛÜ‡J‚J„LÜ×,Ñ,×2°×:Ñ:¸3Ó?€DÜ”]Ð$:¸BÓ?×EÀ2€FÞÜ�oŠoÐ\Ó]Ð]Ü�w‰w�|‰|œBŸG™GŸO™O¬B¯G©G¯O©O¼HÓ,EÓFÈÐPVÐXmÓn€HÜ‡K‚K”—‘—‘ Ó)°DÒ9Ø
€Cõ&áÐ
œx×0Ñ0×4Ñ4Ó6×@Ñ@È)Ð@ÐTÐUÐU^Ð_cÐ^dÐdjÖtzÑkpð  AJð  kKð  	Lô  MÜ	Ô	˜aØ�J‰J×Ñ tÐÐ,ˆà�m‰m‹oˆØ�k‰kˆ÷
	 ð 
	 ö Ø$+¨F¨8Ð"4Ð@RÑSˆAÙÐ?Ô@ÙÐ<Ð>lÐnoÔpÙÐ=Ð?nÐpqÔrÙÐOÔPÛS�Ù˜˜T˜F�m T¨D¡[°!Ö4ñ TáÐJÔKÛR�Ù˜˜T˜F�m T¨D¡[°!Ö4ò Sñ ÐJÔKÙÐXÔYÙÐ3°TÐ<]Ñ5]Ð`hÐj|Ð_}Ô~Ø�	‰	ŒáÐ7Ô8Ü×*Ò*¨1¨kÀDÑIˆØ $§
§
ˆt�z‰z˜!Š}°·±³ˆÜ×,Ò,¨TÓ2ˆÙÐ/°¨vÐ6Ô7Ø�|‰|ˆß ˆDð?Ø—H‘H˜T D™[°8Ð=OÐ2PÐZ_Ðop�HÐq�Ø—v‘v“x�Ø!%§¡£×!8Ñ!8¸×!DÑ!D‘vÐT[Ð_cÐdhÐehÐ_i×_oÑ_oÓ_qÓTqÉ&Ðw}�Ø—i‘i—m‘m J°Ó3�Ù�b˜˜˜g a§h¡h Z¨q°°°a¼¸D»	°{À!ÖVYÀVÈcÂ\Ð_aÐCbÐbdð  CGð  KQó  CQÔesÐtxÐz}Ôe~ð  WYð  eZð  [ö  \ñ !ñ 	ÐEÔFð5	BØ�I‰I�d˜[Ñ(Ð5GÐQVˆIÑWØ×!Ñ! $Ô'Ø—<‘<“>ˆDØ—j’jÐ!HÈ$ÓOˆGÙÐ"¤3 w£< .Ð1Ô2Ü  Ó&ˆEØ˜s “|�Ø Ÿ^™^¨F×3Ñ3‘c¸ÈÏÉÐWZ×H[ÑH[ÁÐadÐgjÑajÑ9k�ð	HØŸ™ ¨e˜Ð4�AØŸ&™&›(�CÜ(¨Ó-�EÙ˜"˜S  ˜Y˜K r¬#¨c«(¨°3´s¸5³z°lÀ*ÐMÔNÛ"˜Ø E�>Ø!ŸL™L¨žOó #ñ $ñ �"”S˜“Z�LÐ 6Ð7Ô8Ø˜4˜C“[�Ù�d˜1˜#�J–ñ !ñ
 ÐRÔSØ%'¨˜(ˆLØ˜s “|�Ø cÓ)ÙØ Ÿ^™^¨F×3Ñ3‘c¸ÈÏÉÐWZ×H[ÑH[ÁÐadÐgjÑajÑ9k�ðØŸ(™( 3°˜(Ð6×;Ñ;Ó=�Cô $ CÓ(�Ù�b˜˜S˜T˜˜ 2¤c¨"£g Y¨nÐ=Ô>Û�AØ Õ,Ø$×+Ñ+¨AÖ.ñ ô $0°Ö#4‘K�A�q˜%Ø Õ(Ø'(¨%°°S°T°Ð&;˜ ›ó $5ñ $ñ" �"”S˜Ó&Ð'Ð'?Ð@ÔAØ! $ 3Ó'�Ù�d˜1˜#�J–ñ (áÐ#¤C¨£M ?°"Ð5Ô6Ø(0¯©Ö(8Ñ$�Ñ$�A�u˜eÙ�d˜1™%˜r !  D¨¨Ð0Ô1Û�AÙ˜* Q C tÐ,Ö-ó ñ )9ñ 	Ð@ÔAð	>Ø—=‘=ð "ó ˆDð
 —M‘MÐ"pÓqˆEÙÐ$ T§Y¡Y¨t¯x©x¸Ó/?×/EÀ2Ó%FÐ$GÐHÔIÙ�,˜tŸx™x¨Ó6Ð7Ð7JÈ4Ï8É8ÐT`ÓKaÐJbÐbeÐfÔgÙÖW\�/ '§.¡.°·±³Ó"@×"JÑ"JÓ"LÈSÈbÐ"QÐ!RÑSÐbzÔ{ÞØ—}‘}“�à'¸ÐJ\Ñ)]Ð^Ø5ÀuÐXjð  AQñ  8Rð  SØ+ÀÈÈÐ?PÐ\nÑ-oÐpó'‘N�E˜7ó
 !s˜ðSØ $§¡× 0Ñ 0°¸±ÀgÐW\ÐlmÐ 0Ð n˜AØ#$§6¡6£8˜DØ-1¯Z©Z«\×-DÑ-DÀZ×-PÑ-P¡6Ð`gÐkoÐptÐqtÐku×k{Ñk{Ók}Ó`}ÑV\ð  DJ˜DÙ " U G¨1¨T¨F°'¸!¿(¹(¸À1ÀTÀFÈ!ÌCÐPTËIÈ;ÐVYÐvzð  Eó  wEÔZhÐimÐoqÔZrð  KMð  ZNð  !Oö  Pó !sñ'ð —
‘
”ð 	�
‰
Œñ 	Ð.Ô/ð:	@ØˆHðØ—X‘X˜dÐ%FÑFÐPU�XÐV×[Ñ[Ó]�ØŸ˜b—~‘~ iÓ0×6°B�Þ@D˜D ™GŸM r×.Ñ.¨|Ô<È$�ñ Ð8¸¸
ÐCÔD÷ ò  ñ4 ˜Ð!8Ó9ˆCÜ˜#œt×$Ñ$Û>�CØŸ7™7 3›<�DÜ! $¬×-Ó-·$°$ÙÐ6°s±g¸QÐ?Ô@Ü/:¸4À¹7Ö/C™O˜D $¨Ù &¨¨¨b°°°d¸3¸%Ð @ÖAñ 0Dáñ ?ñ �'Ð/Ô0Ù�*Ð5Ô6Þà$¨°x°jÐ@RÐ&SÐTØ! ^°H°:¸[Ð#IÐJØ'Ð+;¸H¸:ÐEVÐ)WÐXØ#Ð'GÈÀzÐQ_Ð%`Ðaó	$‘K�E˜4ñ ˜% Ö&ñ$ñ Ð(Ð*JÔKÙ�,Ð >Ô?ñ 	Ð1Ô2ð	EØ—=‘=“?ˆDØ—‘× Ñ  ¨¡¸%Ð Ð@ˆAØ—6‘6“8ˆDØ—J’JÐ1°4¸r¿t¹tÑDˆEÙÐ% a§h¡h Z¨r´#°d³)°¸CÄÀEÃ
¸|È8ÐTÔUØ˜2˜A“Y�Ù�d˜1˜T˜c˜7˜)Ð$Ö%ñ àŸ
š
Ð#JÈDÐXZ×X\ÑX\Ñ]Ð^aÐ_aÓb�Ù�j  Ð'Ö(ñ cä! $Ó'¨¨Ó,�Ù�m A 3Ð'Ö(ñ -ä# D¨"Ö-�Ù�j  Ð$Ö%ñ .ä+¨D°aÔ8‘��1�eÙ�d˜1™%˜r !  F¨5°©8¨*°DÐ9Ö:ñ  9à�J‰JŒLð 	
�‰Œ	÷C 
ôD 
ˆh˜ gÒ	.°!Ø	�‰�—	‘	˜#“ Ñ%Ô&÷ 
/äÐ˜t ´s¸3³x°jÐ@gÐ%hÐjkÐmnÕoøôm ó ?Ù�b˜˜˜j¬¯ª¸Ó);Ð(<Ð=×>Ó>ûð?ûô( !ó HÙ˜"˜S  ˜Y˜K z´$·/²/À!Ó2DÐ1EÐF×GÓGûðHûô  !ó Ù˜"˜S  ˜Y˜K z´$·/²/À!Ó2DÐ1EÐFÔGÞûðûô& ó 	BÙÐ+¬D¯OªO¸AÓ,>Ð+?Ð@×AÒAûð	Bûô>  )ó SÙ " U G¨1¨T¨F°*¼T¿_º_ÈQÓ=OÐ<PÐ Q×RÓRûðSûô ó 	>ÙÐ'¬¯ª¸Ó(:Ð';Ð<×=Ò=ûð	>ûô" ó Úðûôf ó 	@ÙÐ)¬$¯/ª/¸!Ó*<Ð)=Ð>×?Ò?ûð	@ûô( ó 	EÙÐ.¬t¯ª¸qÓ/AÐ.BÐC×DÒDûð	Eú÷ 
Ö	ú÷D 
/Ö	.ús¶  Å.E uËCm:ÎuÎ+B'p1ÑAn5Ò2n5ÓBp1Õo3Õ*/p1Ö+p1×Bp1Ù%uÙ.C=r&Ý,B/q(àr&à3uás.áA*sâ9As.äs.ä#Bs.æ:uçEt%ìuì1$u.í:
n2î"n-î&uî-n2î2uî5
o0î?%o+ï$p1ï+o0ï0p1ï3
p.ï=%p)ð"p1ð)p.ð.p1ð1
q%ð;q ñuñ q%ñ%uñ(
r#	ñ2%r	òr&òr#	ò#r&ò&
sò0sóuósóuó
s+ó's.ó*s+ó+s.ó.
t"ó8tôuôt"ô"uô%
uô/uõuõuõuõ
u+õ.
u=c           
      óÜ  ^• / n0 n0 n[         R                  " 5        H‰  nUR                  S5      =(       d    0 R                  S5      n[         R                  " U5      nU HA  mU(       a  UR	                  TU5        UR	                  T[        U4S jU 5       5      5        MC     M‹     U GHš  n[         R                  " US   5      n	UR                  S5      U	SSUR                  S5      =(       d    SSS	 =(       d    SUR                  S
5      SSS.n
UR                  U	S5      nUR                  U	5      =(       d    [         R                  " U	/UQ76 n U(       d  [        R                  " X	U5      nU(       d  SU
S'   UR                  XŠ45        Mê  [        R                  " X5      n [!        UR                  S5      5      U
S'   [        R&                  " U5      u  nnUSS U
S'   UR                  S5      =(       d    0 n[        [)        UR                  S5      [*        5      (       a)  UR                  S5      =(       d    0 R                  S
5      OUR                  S5      =(       d    S5      =(       d    SU
S'   [        [)        UR                  S5      [*        5      (       a)  UR                  S5      =(       d    0 R                  S
5      OUR                  S5      =(       d    S5      =(       d    SU
S'   U
S   [        R,                  " SS5      :w  a  SU
S    S3U
S'   OXúS'   Uc  SU
S'   UR                  XŠ45        GM�     U$ ! [        R                   a,  n[        U5      U
S'   UR                  XŠ45         SnAGMÜ  SnAf[        R                   a    e [         a&  nSU 3U
S'   UR                  XŠ45         SnAGM  SnAff = f! ["        [$        4 a	    SU
S'    GN f = f)a–  Read each waiting car's own DealerKit record and say what it went
for: one report per car for /api/bidbrain/sales. The vehicle id comes
from the purchase's own dk_check, else the in stock records, else one
stocklist scan. A record that does not read Sold (25) is reported with
its stage and no price: Awaiting Delivery is not a sale (Steven
2026-09-12). Never raises for one car; a dead session does raise.rÞ  rß  c              3   ó6   >#   • U  H  oT:w  d  M
  Uv •  M     g 7frŸ  rG   )rW  rp   r/   s     €r0   rX  Ú&_sale_price_reports.<locals>.<genexpr>D  s   øé € Ð,Jº°1ÀcÁ¯Q©Qºùs   ƒ	�	rW   Úapi_idNÚsold_atrX   r�  rú   )rÉ  rW   Ú
sold_pricerk   Úsold_onrú   Úrecord_keysrÊ  rG   z"not found on DealerKit's stocklistrÊ  zrecord not read, rà  rk   é<   rÍ  Úvehicler  r  ÚSoldzDealerKit reads stage z, not Sold; no price sentrË  z1no figure recognised on the record; keys reported)r   r²  r+   Úpurchase_platesÚ
setdefaultÚtupler   Údealerkit_stock_vehicle_idr	   Ú_find_vehicle_id_by_regrH  rí  rñ  rF  r»  r¦   r  r5  rw  Úsale_price_figurerO  rò  Ústage_number)r°   r¾  ÚreportsÚby_regÚ
also_knownr   Úvidr¬  r‡  rW   rË  Úaltrß  rÿ  rK  r  ri  rq   r/   s                     @r0   Ú_sale_price_reportsrÝ  -  sn  ø€ ð €Gð €FØ€JÜ×ÒÖ ˆØ�u‰u�ZÓ ×& B×+Ñ+¨LÓ9ˆÜ×"Ò" 1Ó%ˆÛˆCÞØ×!Ñ! # sÔ+Ø×!Ñ! #¤uÔ,J¹Ó,JÓ'JÖKó ñ !ô ˆÜ�lŠl˜3˜u™:Ó&ˆØŸ™ Ó*°3ÀdÐVZØŸ7™7 9Ó-×3°°S°bÐ9×A¸TÈ3Ï7É7ÐSYË?ÐkoÐy}ñˆà�n‰n˜S "Ó%ˆØ—Z‘Z “_×P¬×(EÒ(EÀcÐ(PÈCÒ(Pˆ
ð	ÞÜ,×DÒDÀTÐPSÓT�
ÞØB��F‘Ø—‘ ˜zÔ*ÙÜ$×0Ò0°ÓBˆFð	!Ü §
¡
Ð+>Ó ?Ó@ˆC�‰Mô &×7Ò7¸Ó?‰ˆˆtØ! # 2˜YˆˆMÑØ�J‰J�yÓ!×' RˆÜ¼zÈ!Ï%É%ÐPVË-ÔY]×?^Ñ?^˜1Ÿ5™5 ›=×.¨B×3Ñ3°FÔ;Ðde×diÑdiÐjpÓdq×dwÐuwÓx÷  Að  }AˆˆF‰ÜÄÈAÏEÉEÐRYËNÔ\`×AaÑAa˜AŸE™E '›N×0¨b×5Ñ5°fÔ=Ðgh×glÑglÐmtÓgu×g{Ðy{Ó|÷  Eð  AEˆˆG‰Øˆx‰=œO×8Ò8¸¸vÓFÓFØ2°3°x±=°/ÐAZÐ[ˆC�ŠKà %�ÑØ‰}ØQ��F‘Ø�‰˜�z×"ñO ðP €Nøô5 ×2Ñ2ó 	Ü˜a›&ˆC�‰KØ�N‰N˜C˜:Ô&ÞûÜ×6Ñ6ó 	ØÜó 	Ø-¨a¨SÐ1ˆC�‰KØ�N‰N˜C˜:Ô&Þûð	ûô œ:Ð&ó 	!Ø ˆC�ŒMð	!ús<   Å<MÆMÆ-OÍOÍ! NÎ!OÎ)O
Ï
OÏO+Ï*O+c                 óÚ  • [         R                  " U R                  S5      =(       d    S5      nU R                  S5      =(       d    SSS nU R                  S5      nU(       a  [        U5      S:w  d  U(       d  gSn U R                  S5      b  [	        U S   5      OSnUbH  US:¼  aB  [
        R                  R                  U5      [
        R                  " US	9-
  R                  5       nU R                  S
5      =(       d    SR                  5       nU R                  S5      =(       d    SR                  5       nSU R                  S5      =(       d    U SU 3USR                  S UR                  5       UR                  5       4 5       5      UR                  5       UR                  5       SSXB[	        [        [!        U5      5      5      SSSSSS.$ ! [        [        4 a    Sn Nþf = f)zèA Clickdealer sales report row (sales_history) as a sale for Dealer
OS: the report's own net sale price is the deal price, the day it came
in is the sold day less its days to sell. Pure; None without a plate,
a sold date or a price.rW   rX   Ú	sold_dateNr�  Ú
sale_priceÚdays_to_sellr   r:   r  r  zcd:Ústock_numberr¡  r  c              3   ó6   #   • U  H  o(       d  M  Uv •  M     g 7frŸ  rG   ©rW  rû   s     r0   rX  Ú*clickdealer_sale_report.<locals>.<genexpr>„  s   é € ÐKÒ(E 1ËŸQ™QÒ(Eùs   ‚
�	r¿   Údealr   ©rÉ  rW   rú   r  r  ÚspecÚphotoÚin_sincerÌ  rË  rk   Úprice_sourcerc   rÍ  rÊ  )r   r   r+   rš   r  r>   r<   r·   r?   r@   r5  rw  r  rI  Útitler5  rË  )r‡  rW   r6  r  rê  r;   r  r  s           r0   Úclickdealer_sale_reportrí  p  s¢  € ô
 �,Š,�s—w‘w˜u“~×+¨Ó
,€CØ�G‰G�KÓ ×& B¨¨Ð,€DØ�G‰G�LÓ!€EÞ”#�d“)˜r“/®ØØ€HðØ+.¯7©7°>Ó+BÑ+NŒs�3�~Ñ&Ô'ÐTXˆØÑ ¨£	Ü Ÿ™×3Ñ3°DÓ9¼H×<NÒ<NÐTXÑ<YÑY×dÑdÓfˆHð �G‰G�F‹O×!˜r×(Ñ(Ó*€DØ�W‰W�WÓ×# ×*Ñ*Ó,€EØ˜CŸG™G NÓ3×:°sÐ;¸1¸T¸FÐCÈCØ—H‘HÑK¨¯©«°u·{±{³}Ñ(EÓKÓKÐUY×U_ÑU_ÓUaÐlq×lwÑlwÓlyØ 4°XÔ^aÔbgÔhmÐnsÓhtÓbuÓ^vØ¨&¸MÐZ^Ðhlñnð nøô	 ”zÐ"ó ØŠðús   Á>A-G ÇG*Ç)G*c                 ó|   • [        U [        5      (       a&  U R                  5       R                  S5      (       a  U $ S$ )zA photo a browser can show, a web link, else None: never a path on
this Mac's disk (2026-09-13, the first backfill sent those).)zhttp://zhttps://N)rO  rF  r  rn   )rÏ  s    r0   Ú
_web_photorï  ‰  s3   € ô ˜u¤c×*Ñ*¨u¯{©{«}×/GÑ/GÐH_×/`Ñ/`ˆ5ÐjÐfjÐjrE   c                 óÒ  • U R                  S5      (       d  U R                  S5      (       a  gU R                  S5      =(       d    0 n [        UR                  S5      5      nUS:w  a  U R                  S5      =(       d    SS	:w  a  g[        R
                  " U R                  S
5      =(       d    S5      nUR                  S5      =(       d    SSS nU R                  S5      b  U R                  S5      OUR                  S5      nU(       a  [        U5      S:w  d  U(       d  gUR                  S5      =(       d    U R                  S5      =(       d    SSS =(       d    SnSU R                  S5       3X0R                  S5      =(       d    SSSS[        U R                  S5      5      Xd[        [        [        U5      5      5      SSSSSS.$ ! [        [        4 a    Sn GN‚f = f)aq  A purchase this Mac saw through to Sold, as a sale for Dealer OS: the
price is the advertised figure DealerKit locked when it went (listed,
never a deal price, so it never outranks one); the sold day is the
disposed_on day off the DealerKit record check (delivery_on is the
day it came in, not the day it went). Pure; None unless the car is
Sold with a day and a plate.rš  ÚshellNrÞ  rà  r¿   r˜  rX   rÐ  rW   Údisposed_onr�  ræ  rå  rá  r¢  zbb:r«  rú   r—   ÚlistedÚbidbrainrç  )
r+   r  r5  rw  r   r   rš   rï  r5  rË  )r‡  r  râ  rW   r6  r  rê  s          r0   Úbidbrain_sale_reportrõ  �  s”  € ð ‡w�wˆ~×Ñ #§'¡'¨'×"2Ñ"2ØØ	�‰�Ó	×	" €BðÜ�B—F‘FÐ.Ó/Ó0ˆð �ƒ{˜Ÿ™ Ó/×5°2¸&Ó@ØÜ
�,Š,�s—w‘w˜u“~×+¨Ó
,€CØ�F‰F�=Ó!×' R¨¨"Ð-€DØ36·7±7Ð;UÓ3VÑ3bˆC�G‰GÐ.Ô/Ðhj×hnÑhnÐo}Óh~€EÞ”#�d“)˜r“/®ØØ—‘�}Ó%×G¨¯©°Ó)A×GÀRÈÈ"ÐM×UÐQU€HØ˜CŸG™G D›M˜?Ð+°CÇÁÈÃ×AVÐTVÐ`dÐosØ¤:¨c¯g©g°kÓ.BÓ#CÐQYÜœe¤E¨%£LÓ1Ó2¸bÐRZÐfpØ¨ñ/ð /øô ”zÐ"ó Ø‹ðús   Á	G ÇG&Ç%G&c                 ó   ^• SSK nSSKJn  U4S jn[        SS5        [        R
                  " U SSS	9nUR                  (       a  UR                  S   OUR                  5       n UR                  US
SS9  U" U5      (       a  XgS4$ UR                  5         [        SS5        [        R                  " U S[        5       S9nUR                  (       a  UR                  S   OUR                  5       n UR                  US
SS9  SnUS:  a°  U" U5      (       a(   UR                  [        R                  " S5      S9  XgS4$  U" UR                  5      R                   n	SU	;   a'  SU	;  a!  SUR                  ;  a  UR                  US
SS9  [        SSSU-
   S35        UR#                  S5        US-  nUS:  a  M°  XgS4$ ! [         a     GNVf = f! [         a     NÙf = f! [         a     N°f = f! [         a     Nrf = f)aQ  Clickdealer logged in: the saved session headless first, else a
window on the Mac and a wait of up to five minutes for a person, the
way clickdealer_pass does it (Clickdealer's single sign on host must
never be navigated away from mid login). Returns (ctx, page, ok); the
caller owns ctx. The refreshed session is saved for the next run.r   NrR  c                 óH   >•  [        T" U 5      5      $ ! [         a     gf = frh  ri  rj  s    €r0   rl  Ú$_open_clickdealer.<locals>._is_ready´  rn  ro  rT  z+Opening Clickdealer with the saved login...r   Tr†   rˆ   rU  rŠ   zDThe saved Clickdealer login has expired, opening a window on the MacFr;  rV  rj  rW  rX  Úsold_vehiclesrQ  z4Waiting for you to log into Clickdealer on the Mac (r@  r  )r=   r^  rS  r™   r   r�   rž   rŸ   r¡   r¦   r§   rA  r9  rD  rn  rÂ  r`  rC  )
r©   rÂ  rk  rb  rS  rl  r¯   r°   rF  rd  s
     `       r0   Ú_open_clickdealerrú  «  sþ  ø€ ó Ý%õô Ð!Ð#PÔQÜ
×
%Ò
% a¨ÀÑ
F€CØŸ9Ÿ9ˆ3�9‰9�QŠ<¨#¯,©,«.€DðØ�	‰	�#Ð"4¸eˆ	ÑDñ �‡�Ø˜$ˆÐØ‡I�I„KÜÐ!Ð#iÔjÜ
×
Ò
˜q¨5¼_Ó=NÑ
O€CØŸ9Ÿ9ˆ3�9‰9�QŠ<¨#¯,©,«.€DðØ�	‰	�#Ð"4¸eˆ	ÑDð €FØ
�3‹,Ù�T�?‰?ðØ×!Ñ!¤w×'9Ò'9¸-Ó'HÐ!ÑIð ˜d�?Ð"ð	Ù˜DŸH™HÓ%×,Ñ,ˆDØ $Ó&Ð+;À4Ó+GÈOÐcg×ckÑckÓLkØ—	‘	˜#Ð*<Àe�	ÑLô 	Ð%Ð)]Ð^aÐdjÑ^jÐ]kÐkrÐ'sÔtØ
�‰�ŒØ�!‰ˆð �3�,ð  �eÐÐøô; ó Úðûô ó Ùðûô ó Ùðûô ó 	Ùð	úsI   Á%F? Ã2G Ä#G  ÅA	G0 Æ?
GÇGÇ
GÇGÇ 
G-Ç,G-Ç0
G=Ç<G=c                 óV   • [         R                  " X5      nU  SU SU SU SU SU S3$ )Nz+/sold_vehicles.php?start_day=1&start_month=z&start_year=z	&end_day=z&end_month=z
&end_year=z:&user_id=&sale_type=&location_id=&view=&action=View+Report)r   Ú_month_range)r.   r  r_  Úlasts       r0   Ú_clickdealer_sold_urlrþ  ß  sH   € Ü×#Ò# DÓ0€DØˆfÐ?À¸wÀlÐSWÐRXØ˜�v˜[¨¨¨z¸$¸ØHðJð KrE   c                 ó  • U=(       d    [         R                  R                  5       nUR                  UR                  p2/ n[        U 5       H(  nUR                  X#45        US-  nUS:X  d  M"  US-
  Sp2M*     [        [        U5      5      $ )zQ(year, month) for the last months_back months, oldest first, this
month included.r9   r   r‹  )	r>   r<   rI   r  r_  r`  rH  rB  Úreversed)Úmonths_backrI   r8  r³   r,  rj   s         r0   Ú_clickdealer_monthsr  æ  sx   € ð ×*”X—]‘]×(Ñ(Ó*€EØ�:‰:�u—{‘{€qØ
€CÜ�;ÖˆØ�
‰
�A�6ÔØ	ˆQ‰ˆØ��6Ø�q‘5˜"Šqñ	  ô
 ”˜“ÓÐrE   Úsales_photosc           	      óÎ  • SSK n U R                  R                  USS9nUR                  (       d  gUR                  R                  S5      =(       d    SR                  5       nUR                  5       nU(       a  U(       a  UR                  S5      (       d  gSU;   a  S	O	S
U;   a  SOSn[        R                  " [        SS9  [        R                  R                  [        U SUR                  UR                  5       5      R!                  5       SS  U 35      n[#        US5       n	U	R%                  U5        SSS5        U$ ! [         a     gf = f! , (       d  f       U$ = f)a  The car's first photo, fetched with the page's own session and kept
at data/sales_photos/<REG>-<hash of the url>.<ext>, so a different
picture for the same plate is a different file and gets its own link.
Returns the local path, or None when Clickdealer did not give an image.r   NrQ  rR  zcontent-typerX   zimage/Úpngz.pngÚwebpz.webpz.jpgTr6  Ú-é   Úwb)r¦  r«  r+   r   rx  r  r‡  r¦   rn   rì   r8  ÚSALES_PHOTO_DIRrí   rI  Úmd5r®  r¯  ry   rÂ   )
r°   rÂ  rW   r¦  ÚresprQ  r‡  Úextrí   r}   s
             r0   Ú_save_car_photor  ÷  s!  € ó
 ðØ�|‰|×Ñ ¨UÐÐ3ˆØ�w�wØØ—‘× Ñ  Ó0×6°B×=Ñ=Ó?ˆØ�y‰y‹{ˆö –D §¡°×!:Ñ!:ØØ˜T“M‰&°&¸D³.¡wÀf€CÜ‡K‚K”¨$Ò/Ü�7‰7�<‰<œ¨C¨5°°'·+±+¸c¿j¹j»lÓ2K×2UÑ2UÓ2WÐXZÐYZÐ2[Ð1\Ð]`Ð\aÐ)bÓc€DÜ	ˆd�DÔ	˜QØ	�‰�Œ÷ 
à€Køô ó Ùðú÷ 
Ô	à€Kús#   †+E ²AE Ä)EÅ
EÅEÅ
E$c                 ó�  • SSK JnJn  U (       a  SOSn[        R                  " 5         [        5       u  pEU(       a  U(       d  [        R                  " S5      e[        R                  S   S   n[        S5      n[        U/US	   Q76 nS
 n	/ / / / 4u  p«pÍS=n=nn[        5        n[        UX‰5      u  nnn U(       d2  [        USS5        [        S5         UR!                  5         SSS5        g[#        US5       H¯  u  nu  nn[%        SSU SUS SU S['        U5       S3	U['        U5      5         UR)                  [        UUU5      SSS9  UR+                  5       nSU;  a   SU;  a  UR/                  U SUS S35        MŠ  U
R3                  [4        R6                  " U5      5        M±     U (       a  U
S	S n
UR9                  5       n[#        U
S5       GH!  u  nn[;        U5      nU(       d  M  [%        SSUS    S U S['        U
5       S3U['        U
5      5        UR=                  S!5      nSnU(       Ga	   UR)                  U S"U 3SS#S9  UR+                  5       n[4        R>                  " U5      n[4        R@                  " UURB                  5      n U (       at  URE                  5        V!V"s/ s H6  u  n!n"U"(       d  M  [4        RF                  " U!5      (       a  M,  U! S$U"SS%  3PM8     sn"n!SS& U SS'  V#s/ s H  n#S(U#S)S  3PM     sn#-   n[4        RH                  " UU 5      n$[4        RJ                  " UU$U5      nOUS-  nUR=                  S,5      n%SUS,'   U%(       ag  [M        UU%US   5      n&U&(       aP  UR=                  U&5      n'U'(       d3   URO                  U&US   XE5      n'URQ                  U&U'5        U'UU&'   US-  nU'US,'   UR=                  S,5      (       d  US-  nUR/                  U5        GM$     UR!                  5          SSS5        S=n(n)/ n*[U        S['        U5      S.5       H¬  n URW                  XES/SUUUS.-    S0.S1S29n+U([[        U+=(       d    0 R=                  S65      =(       d    S5      -  n(U)[[        U+=(       d    0 R=                  S75      =(       d    S5      -  n)U*[]        U+=(       d    0 R=                  S85      =(       d    / 5      -  n*M®     ['        U5       S9U S:U S;U S<U(       a  S=[_        ['        U5      5      -   S>-   OS? S@U) SAU( SB['        U*5       3n,X½-   U*-   SSC  H  n-[        SDU--   5        M     [        USEU,['        U5      ['        U5      5        [        SFU, SG35        g! [,         a:  nUR/                  U SUS S[        R0                  " U5       35         SnAGMÁ  SnAff = fs  sn"n!f s  sn#f ! [,         a,  nUS-  nS*[        R0                  " U5       3US+'    SnAGN¹SnAff = f! URR                   a:  nUR/                  US    S-[        R0                  " U5       35        Sn' SnAGN‡SnAff = f! UR!                  5         f = f! , (       d  f       GNd= f! URX                   a8  n[        USS3U S['        U5       S4U 35        [        S5U 35         SnA  gSnAff = f)HaÑ  Every sold car off Clickdealer for the last 18 months, each one's
own page read for the full car (year, derivative, engine, fuel,
gearbox, mileage, colour) and its first photo, copied into Dealer OS's
own file store so it outlives the Clickdealer account, then sent to
Dealer OS as the sale (2026-09-14, Steven: "search each car that you
have on record as being sold and find the sales info of that car to
fully back populate it ... just one photo of each sold car in there,
the first one"; 18 months, his choice). Read only on Clickdealer.
probe: one car, the newest, with every field and picture it found
reported for checking before the full run.

Ran for real on 2026-09-14 (749 sold cars, 18 months, two passes) and
then came off the Run menu and the command line at Steven's word:
"i will never have to run this again now all the info is there,
dealerkit will handle all sales moving forward". Kept, with its
readers and tests, as the record of how the history was built.r   )Údealer_os_filesÚdealer_os_syncÚclickdealer_sales_probeÚclickdealer_salesú]Dealer OS is not set up on this Mac (DEALER_OS_BASE_URL and DEALER_OS_API_KEY). Nothing sent.r   rŒ  é   r¨  c                 óD   • U R                  5       nSU;   =(       d    SU;   $ )NÚSoldDateÚDays)re  )r°   rc  s     r0   rk  Ú%clickdealer_sales_pass.<locals>.ready*  s!   € Ø�|‰|‹~ˆØ˜TÑ!×3 V¨t¡^Ð3rE   FzCClickdealer not read, the login was not completed. Nothing changed.z1Clickdealer login not completed. Nothing changed.Nr9   rî  z$Reading Clickdealer's sold cars for r  Ú02drÀ   r”   rB  rˆ   rU  rŠ   rU  r  r  z: report did not renderrØ  rW   z off its Clickdealer page (rß  z/vehicle_details.php?id=r‰   Ú=rT  é-   r�  zimg=i°ÿÿÿzpage not read: rÊ  ré  z photo not stored: rV  ú/api/bidbrain/sales©Úschema_versionÚsalesr[  rR  úSent ú, then: zClickdealer sales stopped: ÚupdatedÚcreatedÚskippedz sold cars sent, z photos stored, z with no photo, z pages not readrE  z months not readrX   ú. Dealer OS added ú, filled in ú, passed over r‹  r•   TzClickdealer sales: rG  )0rô  r  r  r   rÎ   Ú_dealer_os_configr   rM  r   r]  r  rþ  r   rú  rý  r¨   r§   r    r™   rš   r¡   re  r¦   rH  rD  Úextendr   Ú
parse_soldÚ
known_urlsrí  r+   Úparse_vehicle_fieldsÚparse_vehicle_photosrÂ  rm   Úis_private_fieldÚvehicle_detailsÚsold_car_reportr  ÚuploadÚrememberÚDocumentUploadErrorr`  Ú_requestÚDealerOsSyncErrorr  rB  rF  ).r†  r  r  r/   rŒ  r  r.   ÚmonthsÚ	first_urlrk  r¾  ÚunreadrØ  rf  Úphotos_sentÚno_photoÚno_pager©   r¯   r°   r   r±   r8  r³   rc  rK  Úhaver‡  rË  rÛ  Ú
probe_keysr   Úpicsrp   rq   ÚuÚdetailsr—   ÚlocalÚlinkr#  r$  r%  ro   rÌ  r  s.                                                 r0   Úclickdealer_sales_passrD    s  € ÷" 9Þ',Ñ
#Ð2E€CÜ‡J‚J„LÜ)Ó+Ñ€HÞž7Ü�oŠoÐ}Ó~Ð~Ü�=‰=˜Ñ'¨
Ñ3€DÜ  Ó$€FÜ% dÐ8¨V°B©ZÒ8€Iò4ð $& r¨2¨r >Ñ €D�'Ø'(Ð(€KÐ(�(˜WÜ	Ô	˜aÜ)¨!¨YÓ>‰ˆˆT�2ðB	ÞÜ˜˜UÐ$iÔjÜÐIÔJØð| �I‰IŒK÷I 
Ð	ô ' v¨qÖ1‘	�‘6�A�qÜ˜)Ð'KÈAÈ3ÈaÐPQÐRUÈwÐVXÐYZÐX[Ð[_Ô`cÐdjÓ`kÐ_lÐlmÐ%nÐpqÔsvÐw}Ós~ÔðØ—I‘IÔ3°D¸!¸QÓ?ÐL^Ðhm�IÑnØŸ<™<›>�Dð  TÓ)¨f¸DÓ.@Ø—M‘M Q C q¨¨3¨Ð/FÐ"GÔHÙØ—‘œK×2Ò2°4Ó8Ö9ñ 2ö Ø˜B˜C�y�Ø"×-Ñ-Ó/ˆDÜ# D¨!×,‘��3Ü-¨cÓ2�ÞÙÜ˜) x°°E±
¨|Ð;VÐWXÐVYÐY]Ô^aÐbfÓ^gÐ]hÐhiÐ%jÐlmÔorÐswÓoxÔyØ—g‘g˜lÓ+�Ø!�
ßðMØŸ	™	 T FÐ*BÀ3À%Ð"HÐUgÐqv˜	ÑwØ#Ÿ|™|›~˜Ü!,×!AÒ!AÀ$Ó!G˜Ü*×?Ò?ÀÀdÇhÁhÓO˜Þ ØGMÇ|Á|Ä~ô  +DÂ~¹t¸qÀ!ÔYZ«?Ôcn×cÒcð  AB÷  dC«?¨a¨S°°!°C°R°&°«?Á~ò  +Dð  EHð  FHð  +IØGKÈCÈRÁyÓ,QÂyÀ!¨t°A°c°d°G°9Ó-=ÁyÑ,Qñ+R˜Jä"-×"=Ò"=¸fÀdÓ"K˜Ü)×9Ò9¸#¸wÈ
ÓS™ð
 ˜q‘L�Gð  ŸG™G GÓ,�	Ø#��G‘ÞÜ+¨D°)¸SÀ¹ZÓH�EÞØ#Ÿx™x¨›˜Þ#ð,Ø'6×'=Ñ'=¸eÀSÈÁZÐQYÓ'c Ø /× 8Ñ 8¸ÀÔ EØ.2  U¡Ø +¨qÑ 0 ð (,˜˜G™Ø—w‘w˜w×'Ñ'Ø ‘M�HØ—‘˜s×#ñY -ð\ �I‰I�K÷I 
ðJ Ð€GˆgØ€GÜ�1”c˜'“l CÖ(ˆð	Ø×'Ñ'¨Ð;PÐefÐqxÐyzÐ{|ð  @Cñ  |Cð  rDñ  SEð  ORÐ'ð  SˆAð
 	”3˜Ÿ˜R—}‘} YÓ/×4°1Ó5Ñ5ˆØ”3˜Ÿ˜R—}‘} YÓ/×4°1Ó5Ñ5ˆØ”4˜Ÿ˜bŸ™ iÓ0×6°BÓ7Ñ7Šñ )ô �g“,�Ð0°°Ð=MÈhÈZÐWgÐhoÐgpÐpÞBH�$œœS ›[Ó)Ñ)Ð,>Ò>ÈbÐQÐQcÐdkÐclÐlxð  zAð  yBð  BPô  QTð  U\ó  Q]ð  P^ð_€Gà‘ 'Ñ)¨3¨BÓ/ˆÜˆd�T‰kÖñ 0äˆC��w¤ G£¬c°'«lÔ;Ü	Ð ˜y¨Ð
*Õ+øôU !ó Ø—M‘M Q C q¨¨3¨¨r´$·/²/À!Ó2DÐ1EÐ"FÔGÞûðüó0 +DùÚ,Qøô %ó MØ 1™˜Ø(7¼¿ºÈÓ8JÐ7KÐ&L˜˜FŸûðMûð( $3×#FÑ#Fó ,Ø %§¡°°E±
¨|Ð;NÌtÏÊÐ_`ÓOaÐNbÐ-cÔ dØ'+¦ûð,ûð �I‰I�Kú÷I 
Ö	ûðT ×/Ñ/ó 	Ü�C˜ %¨ s¨$¬s°7«|¨n¸HÀQÀCÐ HÔIÜÐ/°¨sÐ3Ô4Ýûð	úsô   Â%Y+Â7YÃY+Ã0AYÄ4,U>Å CYÈ;A7WÊ2W
ËW
Ë!W
Ë/WË<W
Ì4WÍAYÎ2X	Ï6YÏ=Y+Ð6Y=Õ>
WÖ.V=Ö6YÖ=W×Y×W×
X×!X×;YØXØYØ	YØ/YÙYÙYÙYÙY(Ù(Y+Ù+
Y:Ù=[Ú-[ Û [c                  ó  • [         R                  " 5         [        5       u  pU (       a  U(       d  [        R                  " S5      e/ / p2[        S5      (       Ga  [        5        n[        U[        R                  S   S    S3SSS9u  pVn U(       d  UR                  S	5        O©[        R                  " U[        R                  S
   S9n[        S[        U5       S35        Sn	U HN  n
 [        R                   " U
[        R"                  " Xj5      5      nU(       d  M8  UR                  U5        U	S-  n	MP     UR                  SU	 S35        UR)                  5          SSS5        OUR                  S5        [         R*                  " 5       n UR-                  S5      R/                  5        Vs/ s H  n[1        U5      PM     nnUR)                  5         S U 5        Vs/ s H  oî(       d  M  UPM     nnUU-  nUR                  S[        U5       S[        U5       35        SSKJn  UR7                  [         R8                  " 5       5      nS U 5        Vs/ s H  oî(       d  M  UPM     nnUU-  nUR                  S[        U5       35        U Vs0 s Hk  n[;        UR=                  S5      5      (       d  M$  [         R>                  " UR=                  S5      =(       d    S5      [;        UR=                  S5      5      _Mm     nnU HV  nUR=                  S 5      (       a  M  UR=                  UR=                  S5      =(       d    S5      (       d  MK  UUS      US '   MX     SS!KJ n  S=nn/ n[C        S[        U5      S"5       H¬  n URE                  XS#SUUUS"-    S$.S%S&9nU[K        U=(       d    0 R=                  S,5      =(       d    S5      -  nU[K        U=(       d    0 R=                  S-5      =(       d    S5      -  nU[M        U=(       d    0 R=                  S.5      =(       d    / 5      -  nM®     [        U5       S/S0RO                  U5       S1U S2U S3[        U5       3	nUSS4  H  n[        S5U-   5        M     [I        S'SU[        U5      [        U5      5        [        S6U S735        g! [        R$                   a    e [&         a  n[        SU
 SU 35         SnAGM  SnAff = f! UR)                  5         f = f! , (       d  f       GNž= fs  snf ! UR)                  5         f = fs  snf s  snf s  snf ! URF                   a8  n[I        S'S(S)U S[        U5       S*U 35        [        S+U 35         SnA  gSnAff = f)8aP  Build Dealer OS's sales history from what this Mac already knows
(2026-09-13, Steven: "take what you already know from clickdealer
history, bidbrain history on my Mac Studio and dealerkit history to
build up the backfill of sales"). Three sources, sent in the order
Dealer OS should trust them: DealerKit's own sold cars, each record
read once for its sold_for (the saved login, no window); the
Clickdealer sales reports already in sales_history; and this Mac's
own purchases that went Sold, at the listed price, which only ever
fill a gap. One off: run it again and nothing is written twice.r  r#   r   rŒ  r¦  r§  Tr‚  z/DealerKit not read, the saved login has expiredrÐ  ©rk   z  DealerKit lists z
 sold carsr   z	  record z: not read, Nr9   r¬  z soldz%DealerKit off, its sold cars not readzSELECT * FROM sales_historyc              3   ó8   #   • U  H  n[        U5      v •  M     g 7frŸ  )rí  rä  s     r0   rX  Ú&sales_backfill_pass.<locals>.<genexpr>·  s   é € ÐBº'°QÔ-¨a×0Ð0º'ùré  zClickdealer r”   ©r  c              3   ó8   #   • U  H  n[        U5      v •  M     g 7frŸ  )rõ  rä  s     r0   rX  rH  À  s   é € ÐA²y°!Ô*¨1×-Ð-²yùré  z	BidBrain r—   rW   rX   ré  ©r  rV  r  r  r[  rR  Úsales_backfillFr!  r"  zSales backfill stopped: r#  r$  r%  z sent, z; r&  r'  r(  r‹  z  passed over: zSales backfill: rG  )(r   rÎ   r)  r   rM  rc  r   r„  r   r]  rH  r	   Úlist_stocklist_idsÚSTAGE_NUMBERSr¨   rš   Úsold_record_reportrí  r»  r¦   r§   ÚconnectÚexecuteÚfetchallrò  rô  r  Ú
with_linksr²  rï  r+   r   r  r`  r5  r6  rý  r  rB  rI  )rŒ  r  rØ  rf  r©   r¯   r°   rG  r‘  r  rÛ  rË  rK  Úconnr   Úcd_rowsÚcdr  Ú	purchasesÚbbrû   Úphotosr  r#  r$  r%  r±   ro   rÌ  r  s                                 r0   Úsales_backfill_passrZ  ‰  s  € ô ‡J‚J„LÜ)Ó+Ñ€HÞž7Ü�oŠoÐ}Ó~Ð~Ø˜ˆUÜÐ1×2Ò2ÜÔ !Ü#2°1¼¿¹À{Ñ9SÐT^Ñ9_Ð8`Ð`iÐ6jØ3NÐVZñ$\Ñ ˆC�yðÞ Ø—L‘LÐ!RÕSä)×<Ò<¸TÌ/×JgÑJgÐhnÑJoÑp�CÜÐ.¬s°3«x¨j¸
ÐCÔDØ�CÛ"˜ð%Ü"1×"DÒ"DÀSÌ/×JeÒJeÐfjÓJpÓ"q˜C÷ ˜3Ø#ŸN™N¨3Ô/°¸±²ñ  #ð —L‘L :¨c¨U°%Ð!8Ô9à—	‘	•÷- Ðð0 	�‰Ð<Ô=Ü�:Š:‹<€DðØ$(§L¡LÐ1NÓ$O×$XÑ$XÔ$ZÓ[Ò$Z˜q”4˜–7Ñ$ZˆÐ[à�
‰
ŒÙB¹'ÔBÓ	HÒB�Ãa�!ÑB€BÐ	HØˆr�M€GØ	‡L�L�<¤ B£˜y¨¬S°«\¨NÐ;Ô<Ý(ð
  ×*Ñ*¬2×+<Ò+<Ó+>Ó?€IÙA±yÔAÓ	GÒA�ÃQ�!ÑA€BÐ	GØˆr�M€GØ	‡L�L�9œS ›W˜IÐ&Ô'ñ Ybó  EÒXaÐSTÔeoÐpq×puÑpuð  wBó  qC÷  fDÓNŒb�lŠl˜1Ÿ5™5 ›<×-¨2Ó.´
¸1¿5¹5ÀÓ;MÓ0NÒNÑXa€Fð  EÛˆØ�w‰w�w×Ó F§J¡J¨s¯w©w°u«~×/CÀ×$DÓ$DØ! # e¡*Ñ-ˆC�‹Lñ õ (ØÐ€GˆgØ€GÜ�1”c˜'“l CÖ(ˆð	Ø×'Ñ'¨Ð;PÐefÐqxÐyzÐ{|ð  @Cñ  |Cð  rDñ  SEð  ORÐ'ð  SˆAð
 	”3˜Ÿ˜R—}‘} YÓ/×4°1Ó5Ñ5ˆØ”3˜Ÿ˜R—}‘} YÓ/×4°1Ó5Ñ5ˆØ”4˜Ÿ˜bŸ™ iÓ0×6°BÓ7Ñ7Šñ )ô �W“�˜g d§i¡i°Ó&6Ð%7Ð7IÈ'ÈÐR^Ð_fÐ^gÐguÔvyð  {Bó  wCð  vDð  E€GØ˜˜“ˆÜÐ $Ñ&Ö'ñ äÐ˜d G¬S°«\¼3¸w»<ÔHÜ	Ð˜W˜I QÐ
'Õ(øôm  /×FÑFó "Ø!Ü(ó %Ü! I¨c¨U°,¸q¸cÐ"BÔCÞ$ûð%ûð —	‘	•ú÷- Öüò6 \øà�
‰
�üÚ	Hùò 
Hùò Eøð ×/Ñ/ó 	ÜÐ$ e¨u°Q°C°t¼CÀ»L¸>ÈÐRSÐQTÐ-UÔVÜÐ,¨Q¨CÐ0Ô1Ýûð	ús°   Á$'TÂAS3Ã++R5ÄS3Ä/S3ÅTÆ!T Æ0TÇT Ç"
T4Ç0T4É
T9É)T9Ê#T>Ê>AT>ÎUÒ5S0ÓS+Ó$S3Ó+S0Ó0S3Ó3TÔTÔ
TÔT ÔT1ÕVÕ-VÖVc                  ón  • [         R                  " 5         [         R                  " 5       n U (       d  [        SSSSS5        g[	        S5      (       d  [
        R                  " S5      e[        5       u  p[        5        n[        U[        R                  S   S	    S
3SSS9u  pEn U(       d?  [         R                  " SSS5        [        SSS5         UR                  5         SSS5        g[         R                  " SSS5        [        XP5      nUR                  5          SSS5        SSKJn  Sn	U(       a4  U(       a-   UR#                  XSSW V
Vs/ s H  u  p«UPM	     snn
S.5      n	OSS0n	[)        U	[*        5      =(       a    U	R-                  S5      SL nSnW HÇ  u  pûUS    S3UR-                  S5      (       a	  SUS   S 3OUR-                  S5      =(       d    S-   n[/        SU-   UR-                  S 5      (       a  S!S"R1                  US    SS# 5       S$3OS-   5        UR-                  S5      (       a  US-  nU(       d  M­  [         R2                  " US%   U5        MÉ     [5        U5       S&U S'3U(       a  SO*S([)        U	[*        5      (       a  U	R-                  S5      OU	 3-   n[        SUU[5        U5      [5        U5      5        [/        S)U S*35        g! UR                  5         f = f! , (       d  f       GNÛ= fs  snn
f ! UR$                   a  nS['        U5      0n	 SnAGNÀSnAff = f)+ag  What a sold car went for (2026-09-12): Dealer OS queues one
sale.price action the moment DealerKit says a car is Sold, the hook
puts the car on sale_price_reads and starts this. No window: the
saved DealerKit login, one record read per waiting car, one POST to
Dealer OS's /api/bidbrain/sales with every answer. Nothing on a
schedule and never the whole feed.Údealerkit_sale_pricesTúNothing waiting.r9   Nr#   zODealerKit purchases push is turned off in Settings > Connections. Nothing read.r   rŒ  r¦  r§  r‚  Fú&The saved DealerKit login has expired.úMNot read, the saved DealerKit login has expired. The daily run logs in again.rX   r   rK  r  r  rï  z$Dealer OS is not set up on this Mac.r   rW   rU  rË  u   sold for Â£r3  rÊ  zno pricer•   rÍ  z [rE  râ  Ú]r«  ú read, z pricedz, not sent: zDealerKit sale prices: rG  )r   rÎ   Úpending_sale_price_readsrý  rc  r   rM  r)  r   r„  r   r]  rE  r§   rÝ  rô  r  r5  r6  rF  rO  rò  r+   r¨   rI  Úfinish_sale_price_readrš   )r¾  rŒ  r  r©   r¯   r°   rG  rØ  r  Úanswerrj   rË  rK  Úsent_okre  r‡  r  rÌ  s                     r0   Údealerkit_sale_prices_passrf  ß  s"  € ô ‡J‚J„LÜ×&Ò&Ó(€DÞÜÐ'¨Ð/AÀ1ÀaÔHØÜÐ 5×6Ñ6Ü�oŠoÐoÓpÐpÜ)Ó+Ñ€HÜ	Ô	˜aÜ.¨q´W·]±]À;Ñ5OÐPZÑ5[Ð4\Ð\eÐ2fØ/JÐRVñ XÑˆ�9ð	ÞÜ×%Ò% k°5Ð:bÔcÜÐ/°ð  9Hô  IØð �I‰IŒK÷ 
Ð	ô ×!Ò! +¨t°RÔ8Ü)¨$Ó5ˆGà�I‰I�K÷ 
õ (Ø€FÞ–Gð	'Ø#×,Ñ,¨XÐ@UØ@AÑ_fÔLgÒ_fÑU[ÐUVËSÑ_fÒLgÑ-hój‰Fð
 ÐAÐBˆÜ˜¤Ó&×C¨6¯:©:°dÓ+;¸tÐ+C€GØ€FÛ‰ˆØ�e‘*�˜RÐ È3Ï7É7ÐS_×K`ÑK` k°#°lÑ2CÀAÐ1FÑ$GÐgj×gnÑgnÐouÓgv÷  hEð  {Eñ  GˆÜˆd�T‰kÈ#Ï'É'ÐR_×J`ÑJ`˜r $§)¡)¨C°Ñ,>¸rÀÐ,BÓ"CÐ!DÀAÑFÐfhÑiÔjØ�7‰7�<× Ñ Ø�a‰KˆFçˆ7Ü×%Ò% c¨$¡i°Ö6ñ ô �W“�˜g f X¨WÐ5¾w¹ÈlÔr|ð  ~Dô  FJ÷  sKñ  sKÐ[a×[eÑ[eÐfmÔ[nð  QWð  [Xð  MYñ  Z€GÜÐ# W¨g´s¸7³|ÄSÈÃ\ÔRÜ	Ð# G 9¨AÐ
.Õ/øð/ �I‰I�Kú÷ 
Ö	üó" MhøØ×/Ñ/ó 	'Øœs 1›vÐ&ŽFûð	'úsZ   Á>'K4Â&,KÃK4Ã,#KÄK4Ä?L ÅLÅ 
L ËK1Ë1K4Ë4
LÌL ÌL4ÌL/Ì/L4c                  óø  • SSK Jn   [        R                  " 5         [        R                  " 5       nU(       d  [        SSSSS5        gS [        R                  S	4S
 [        R                  S4S [        R                  S4S [        R                  S4S.n0 nU H'  nUR                  US   / 5      R                  U5        M)     S=pVUR                  5        GHn  u  pxUR                  U5      n	U	cM  U HE  n[        R                  " US   U R                  SU S3UR                  S5      5      5        US-  nMG     Mg  U	u  p«n/ nU H”  n[        R                   " US   5      [        R"                  :”  aU  [        R                  " US   U R                  SU S[        R"                   S3UR                  S5      5      5        US-  nMƒ  UR                  U5        M–     U(       d  GM  [%        S['        U5       SU 35        [)        5        n U
" U5      n UR,                  (       a  UR,                  S   OUR/                  5       nU HÞ  nUR                  S5      (       d"  U R                  SUR                  S5      5      nO U" UUS   UR                  S5      5      n[        R                  " US   U5        UR                  S"5      (       a  US-  nOUS-  n[%        S#U S$UR                  S%5      =(       d    US&    S 3UR                  S"5      (       a  US"   OS'-   5        Mà     UR7                  5          SSS5        GMq     U S(U S)3n[        SUS:„  =(       d    US:H  UXV-   XV-   5        [%        SU S*35        g! [*         aa  nU HG  n[        R                  " US   U R                  SU SU 3UR                  S5      5      5        US-  nMI      SnASSS5        GM  SnAff = f! [*         aS  nU R                  U S[1        U5      R2                   S [5        U5      SS!  3UR                  S5      5      n SnAGN¨SnAff = f! UR7                  5         f = f! , (       d  f       GM   = f)+ae  The shortlist deep read (Dealer OS v4.1.0, Steven 2026-09-17): Dealer
OS's shortlist screen shows one starred car at a time with everything
on one page, and "Read this car" or "Read them all now" there logs a
shortlist.read action naming only the starred cars. The sync queues
them (db.queue_deep_reads) and starts this. One headless session per
platform, the saved login, the car's own page read once, the answer
kept on the row (db.finish_deep_read) and carried under each car's
deep block by the next push, which the server sends the moment this
pass ends. A platform this Mac has no car page read for, a dead
login, or a page that would not load is answered with an error in
plain words, never left blank, so the screen can say why. Never a
bid, never any action on the page (golden rule 1); every field the
page did not show is null, never a guess (golden rule 4).r   )Ú	deep_readrh  Tr]  r9   Nc                 ó.   • [         R                  " U SSS9$ ©Nr   Tr†   ©r   r�   ©r©   s    r0   rË   Ú deep_read_pass.<locals>.<lambda>(  s   € œw×:Ò:¸1¸jÐSWÒXrE   rP   c                 ó.   • [         R                  " U SSS9$ ©Nr   Tr†   rk  rl  s    r0   rË   rm  )  s   € œW×8Ò8¸¸HÈtÒTrE   rQ   c                 ó.   • [         R                  " U SSS9$ )Nr   Tr†   rk  rl  s    r0   rË   rm  *  s   € ¤7×#>Ò#>¸qÀ.Ð[_Ò#`rE   rR   c                 ó.   • [         R                  " U SSS9$ )Nr   Tr†   rk  rl  s    r0   rË   rm  +  s   € ¤× ;Ò ;¸A¸{ÐUYÒ ZrE   rT   )r   r   r   r   rs  r«  zNot read on z+ yet: this Mac has no car page read for it.Úasked_atzCould not read the z page after z tries.zShortlist deep read: z car(s) on zCould not open a z
 session: r˜   z)The card carries no listing link to read.z page read failed: rU  r[  rï  r•   r  rW   r  r.  ra  ú could not be readrG  )rô  rh  r   rÎ   Úpending_deep_readsrý  r   r   r   r   rÒ  rH  rm   r+   Úfinish_deep_readÚfailedÚstart_deep_readÚDEEP_READ_MAX_ATTEMPTSr¨   rš   r   r¦   rž   rŸ   rM  rN  rF  r§   )Ú_deepr¾  ÚopenersÚby_platformr   r.  rv  rs  r¥   ÚopenerÚopen_ctxÚreaderrf   Útodor©   r¯   rK  r°   r�   rÌ  s                       r0   Údeep_read_passr€    sE  € õ ,Ü‡J‚J„LÜ× Ò Ó"€DÞÜ�˜TÐ#5°q¸!Ô<ØáXÔZb×ZlÑZlÐnxÐyÙTÔV\×VfÑVfÐhpÐqÙ`Ôbn×bxÑbxð  {Ið  JÙZÔ\e×\oÑ\oÐq|Ð}ñ	€Gð €KÛˆØ×Ñ˜q ™}¨bÓ1×8Ñ8¸Ö;ñ àÐ€DØ&×,Ñ,×.‰ˆØ—‘˜XÓ&ˆØ‰>Û�Ü×#Ò# A d¡G¨U¯\©\Ø" 8 *Ð,WÐXÐZ[×Z_ÑZ_Ð`jÓZkó.mô nà˜!‘’ñ ñ Ø"(Ñˆ˜%ØˆÛˆAÜ×!Ò! ! D¡'Ó*¬R×-FÑ-FÓFÜ×#Ò# A d¡G¨U¯\©\Ø)¨%¨°¼R×=VÑ=VÐ<WÐW^Ð_Ðab×afÑafÐgqÓaró.tô uà˜!‘’à—‘˜A–ñ ö ÚÜÐ%¤c¨$£i [°¸E¸7ÐCÔDÜÔ !ðÙ˜q“k�ðØ'*§y§y�s—y‘y ’|°c·l±l³n�Û�AØŸ5™5 ×/Ñ/Ø!ŸL™LÐ)TÐVW×V[ÑV[Ð\fÓVgÓh™ðAÙ & t¨Q¨}Ñ-=¸q¿u¹uÀZÓ?PÓ Q˜Aô ×'Ò'¨¨$©°Ô3Ø—u‘u˜W—~‘~Ø !™™à ™	˜Ü˜B˜u˜g Q q§u¡u¨U£|×'C°q¸±|Ð&DÀBÐGÐYZ×Y^ÑY^Ð_f×YgÑYgÈ1ÈWÊ:ÐmsÑtÖuñ ð —	‘	•÷3 Òñ) /ð\ ��g˜f˜XÐ%7Ð8€GÜˆK˜ ™×0 V¨q¡[°'¸4¹=È$É-ÔXÜ	Ð! ' ¨!Ð
,Õ-øô3 ó Û�AÜ×'Ò'¨¨$©°·±Ð@QÐRWÐQXÐXbÐcdÐbeÐ>fÐhi×hmÑhmÐnxÓhyÓ1zÔ{Ø˜a‘K’Fñ ó ÷ Òûðûô  )ó AØ %§¡°¨wÐ6IÌ$ÈqË'×JZÑJZÐI[Ð[]Ô^aÐbcÓ^dÐeiÐfiÐ^jÐ]kÐ-lÐno×nsÑnsÐt~Ónó  !AžAûðAûð —	‘	•ú÷3 ×úsv   ÈQ)ÈNÈA-QÊO4Ê$B
QÌ.Q)Î
O1ÎAO,ÏQ)Ï,O1Ï1Q)Ï4
Q	Ï>AQ	ÑQÑQ	ÑQÑQ&Ñ&Q)Ñ)
Q9	c            	      ó$  • [         R                  " 5         [        S5      (       d  [        R                  " S5      e[        [         R                  " 5       5      n U (       d  [        SSSSS5        g[        5        n[        U[        R                  S   S	    S
3SSS9u  p#n U(       d?  [         R                  " SSS5        [        SSS5         UR                  5         SSS5        g[         R                  " SSS5        [        X05      u  pVnUR                  5          SSS5        [         R                  " 5       (       a   [!        5         ['        U 5       S3n	W(       a	  U	SU S3-  n	W(       a	  U	SU S3-  n	W(       a  U	SU SUS:w  a  SOS S3-  n	[        SSU	['        U 5      ['        U 5      5        [%        SU	 S35        g! UR                  5         f = f! , (       d  f       NÂ= f! ["         a  n[%        SU 35         SnAN½SnAff = f)a°  The quiet stage check (2026-09-07, Steven: "the dealer os purchases
list needs to be in sync with dealerkit as quickly as possible"):
every few minutes serve.py starts this when the Mac is idle. No
window, ever: the saved DealerKit login (refreshed by itself when it
can), API reads only, one per live car on DealerKit, then the same
stage matching the daily run does. A login that cannot be fixed
quietly is left for the daily run.r#   r—  Údealerkit_stagesTzNo live cars on DealerKit.r9   Nr   rŒ  r¦  r§  r‚  Fr^  r_  rX   z"  sale prices not read this pass: z readrE  z checked inz movedz retail pricer3  ú syncedzDealerKit stages: rG  )r   rÎ   rc  r   rM  r¸  r²  rý  r   r„  r   r]  rE  r§   r¹  rb  rf  r¦   r¨   rš   )
r¾  r©   r¯   r°   rG  rý  rþ  re  rK  rÌ  s
             r0   Údealerkit_stages_passr„  d  sè  € ô ‡J‚J„LÜÐ 5×6Ñ6Ü�oŠoÐrÓsÐsÜœR×.Ò.Ó0Ó1€DÞÜÐ" DÐ*FÈÈ1ÔMØÜ	Ô	˜aÜ.¨q´W·]±]À;Ñ5OÐPZÑ5[Ð4\Ð\eÐ2fØ/JÐRVñ XÑˆ�9ð	ÞÜ×%Ò% k°5Ð:bÔcÜÐ*¨Eð  4Cô  DØð �I‰IŒK÷ 
Ð	ô ×!Ò! +¨t°RÔ8Ü(9¸$Ó(EÑ%ˆJ˜và�I‰I�K÷ 
ô 
×"Ò"×$Ñ$ð	<Ü&Ô(ô �T“�˜5Ð!€GÞØ�R˜
�| ;Ð/Ñ/ˆÞØ�R˜�w˜fÐ%Ñ%ˆÞØ�R˜�x˜}°F¸a³K©SÀRÐ,HÈÐPÑPˆÜÐ  g¬s°4«y¼#¸d»)ÔDÜ	Ð˜w˜i qÐ
)Õ*øð! �I‰I�Kú÷ 
Õ	ûô" ó 	<ÜÐ6°q°cÐ:×;Ñ;ûð	<úsH   Á;'GÂ#,GÃGÃ)&GÄGÅ
G- ÇGÇGÇ
G*Ç-
HÇ7H
È
Hc                  ó²	  • SSK n [        R                  " 5         [        S5      (       d  [        R
                  " S5      e[        R                  " 5       nU(       d  [        SSSSS5        [        S5        g[        S	[        U5       S
35        S=p#[        5        n[        U[        R                  S   S    S3S5      u  pVn [        R                  " SUU(       a  SOS5        U(       d2  [        SSS5        [        S5         UR                  5         SSS5        g UR!                  [        R"                  " S5      S9  ['        SS5        [(        R*                  " U5      n['        SS	[        U5       S3S[        U5      5        [-        U5       GHV  u  pšU
S   n [(        R.                  " U
5      n[(        R0                  " X‹U5      u  pÞ[(        R2                  " XkXÜS9nU(       a%  U(       a  U[        R4                  " U5      :w  a  XïS'   [        R6                  " X¿5        US-  nU(       aZ  US-  nUR9                  S5      (       a  SU S3OSn[        SU U SUS    S US!    S"US#   b  S$[;        US#   5      -   OS% 3	5        O/U(       a  S&S'R=                  U5       3OSn[        SU S(U 35         ['        SS*U	S-    S+[        U5       SU 3U	S-   [        U5      5        GMY     UR                  5          SSS5        [        SSS*U S+[        U5       S0U S13[        U5      [        U5      5        [        S*U S+[        U5       S0U S135        g! [$         a     GN%f = f! [(        R>                   Ga
  n[(        R@                  " UU5      (       aa  [        R6                  " US5        US-  n[        SU S)35        ['        SS*U	S-    S+[        U5       SU 3U	S-   [        U5      5         SnAGMj  [        R                  " SS[;        U5      5        [        SSS,U S+[        U5       S-35        [        SU SU 35        [        S,U S+[        U5       S.35         SnA  UR                  5         SSS5        gSnAf[$         a  n[        SU S/U 35         SnAGNïSnAff = f! UR                  5         f = f! , (       d  f       GNÎ= f)2aÌ  On demand: check what is REALLY on every purchase's DealerKit
record right now (2026-08-26, Mark: "add a check all DK records to the
sync icon on the nav bar"), independent of BidBrain's own
dk_*_pushed_at flags, which only ever record that a push was
attempted, not that the data survived. One continuous headed
DealerKit session for the whole batch, same lesson as
dealerkit_purchases_pass; unlike that push pass, the reg lookup and
every check itself are pure API reads (dealerkit_write._server_count
and friends), so this is fast once logged in, no per car clicking or
navigation at all. Reads the whole stocklist once up front
(_stocklist_reg_map) rather than repeating a per reg scan for every
single purchase.r   Nr#   r—  Údealerkit_checkTzNo purchases to check.r9   ú	Checking z/ purchases against their real DealerKit recordsr   rŒ  r¦  r§  rX   r�  Fz6Not run, the login was not completed. Nothing changed.z/DealerKit login not completed. Nothing checked.rj  rä  zReading the DealerKit stocklistz
 purchasesrW   )rß  Úalt_regsÚmatched_platez (found as rB  r•   rU  Úimagesz	 images, Ú	documentsz documents, purchase r¢  rb  ru  z or rE  z: no DealerKit recordz9: DealerKit record no longer answers, marked as no recordzChecked r”   zStopped after z, the session diedz, the session died mid batch.rƒ  z purchases, z have a DealerKit record.)!r=   r   rÎ   rc  r   rM  r²  rý  r¨   rš   r   r„  r   r]  rE  r§   rD  rn  r¦   r™   r	   rê  r    rì  rë  Úcheck_dealerkit_recordr   rõ  r+   rF  rI  r»  rï  )rb  rW  Úcheckedrµ  r©   r¯   r°   rG  rü  r±   r‡  rW   rÜ  rß  Úon_plateÚresultÚunderÚextrarK  s                      r0   Údealerkit_check_passr’  ‘  sÉ  € ó Ü‡J‚J„LÜÐ 5×6Ñ6Ü�oŠoÐrÓsÐsÜ×!Ò!Ó#€IÞÜÐ! 4Ð)AÀ1ÀaÔHÜÐ&Ô'Øä	ˆI”c˜)“nÐ%Ð%TÐ
UÔVØÐ€GÜ	Ô	˜aÜ.¨q´W·]±]À;Ñ5OÐPZÑ5[Ð4\Ð\eÐ2fð  iDó   EÑˆ�9ð>	Ü×!Ò! +¨yÞ)2¡2Ð8`ôbæÜÐ)¨5Ð2jÔkÜÐGÔHØðp �I‰IŒK÷A 
Ð	ðØ×!Ñ!¤w×'9Ò'9¸+Ó'FÐ!ÑGô �iÐ!BÔCÜ%×8Ò8¸Ó>ˆGä�i 9¬S°«^Ð,<¸JÐ!GÈÌCÐPYËNÔ[Ü# I×.‘�Ø˜%‘j�ð)3Ü)×5Ò5°cÓ:�CÜ+:×+NÒ+NÈwÐ]`Ó+aÑ(�JÜ,×CÒCØ¨jñH�Fæ¦(¨x¼2¿<º<ÈÓ;LÓ/Lð 3;˜Ñ/Ü×*Ò*¨3Ô7Ø˜q‘L�GÞØ ™
˜Ø=C¿Z¹ZÈ×=XÑ=X +¨h¨Z°qÑ 9Ð^`˜Ü  3 %¨ w¨b°¸Ñ1AÐ0BÀ)ÈFÐS^ÑL_ÐK`ð a*ØQWÐXgÑQhÑQt¨$´°V¸OÑ5LÓ1MÒ*Mð  {Dð  *EðFõ Gö <? $ t§y¡y°£~Ð&6Ñ 7ÀB˜Ü  3 %Ð'<¸U¸GÐDÕEô. ˜) x°°A±¨w°d¼3¸y»>Ð:JÈ"ÈSÈEÐ%RÐTUÐXYÑTYÔ[^Ð_hÓ[i×jñY /ð\ �I‰I�K÷A 
ôD Ð˜tØ�w�i˜t¤C¨	£NÐ#3°<À¸wÐF_Ð`Ü�	‹NœC 	›Nô,ô 
ˆH�W�I˜T¤# i£.Ð!1°¸e¸WÐD]Ð
^Õ_øôu ó Úðûô: '×>Ñ>ô Ü&×>Ò>¸tÀQ×GÑGä×.Ò.¨s°DÔ9Ø 1™˜Ü  3 %Ð'`ÐaÔbÜ! )¨x¸¸A¹°w¸dÄ3ÀyÃ>ÐBRÐRTÐUXÐTYÐ-ZÐ\]Ð`aÑ\aÔcfÐgpÓcqÔrÞ ô ×)Ò)¨+°u¼cÀ!»fÔEÜÐ-¨u¸ÀwÀiÈtÔTWÐXaÓTbÐScÐcuÐ6vÔwÜ˜B˜s˜e 2 a S˜/Ô*Ü˜N¨7¨)°4¼¸I»Ð7GÐGdÐeÔfÜð
 �I‰IŒK÷A 
Ð	ûôx !ó 3Ü˜B˜s˜e :¨a¨SÐ1×2Ò2ûð3ûð �I‰I�Kú÷A 
Ö	ús­   Â(SÃA R2ÄSÄ #MÅAR2Æ C+M,ÊR2Ê.M,Ê:5R2Ë/SÍ
M)Í%R2Í(M)Í)R2Í,R/ÎA6RÏ7R2Ï>A*RÑ(R2Ñ-SÒR/ÒR*Ò$R2Ò*R/Ò/R2Ò2SÓSÓ
Sc                  ó8  • [         R                  " 5          [        5       u  pp#X-    Vs/ s H5  oDR                  R                  (       d  M  UR                  R                  PM7     nn[         R
                  " U5      nU(       a  [        SU S35        S=p‰SS [        R                  4S	S
 [        R                  4SS [        R                  44 GHý  u  p«n[         R                  " U
5      nU(       d  M&  U[        U5      -  n[        S[        U5       SU
 35        [        SS[        U5       SU
 S3S[        U5      5        Sn[        5        n U" U5      n UR"                  (       a  UR"                  S   OUR%                  5       n['        U5       Hø  u  nn[(        R*                  " SUR-                  S5      =(       d    S5      nU(       d  [         R                  " US   U
SS5        MZ   U" UUR/                  S5      [1        US   5      5        [         R                  " US   U
S5        US-  n[        SUS    SUS   (       a  SOS  S!U
 35        [        SSU
 S%US-    S&[        U5       SUS    3US-   [        U5      5        Mú     UR5                  5          SSS5        Xž-  n	[        SU S&[        U5       S'U
 35        GM      [7        S(US:H  =(       d    U	S:„  U(       a  U	 S&U S)35        gS*5        gs  snf ! [         a  n[        SU S35         SnAGN•SnAff = f! [         aS  n[        SU
 SU 35        U H(  n[         R                  " US   U
S[!        U5      5        M*      SnASSS5        GM·  SnAff = f! [         aW  n[         R                  " US   U
S[!        U5      [1        [3        US"S5      5      S#9  [        SUS    S$U 35         SnAGN‘SnAff = f! UR5                  5         f = f! , (       d  f       GNk= f)+a~
  Push every pending star/unstar to its own platform's real shortlist
or watchlist (2026-08-27, Mark: "when we shortlist a car in bidbrain
add that to watched/tracked in auction sites"), approved by Steven
2026-06-09 as the one platform action BidBrain is ever allowed to
take, never a bid. serve.py's own /api/bid handler queues the desired
state (db.queue_watchlist_sync) the instant a star is toggled and
kicks this pass off in the background; this is also reachable on
demand from the Run modal to catch up any backlog (a queued item that
arrived while another headed pass held the shared profile, for
example).

Right Drive's own second Motorway account, accounts@rightdrive.co.uk
(Mark, same day: "we have 2 accounts for MW as to make a purchase it
has 2fa via sms and so can we add another account to push the watched
cars from BB to both accounts"), is treated as its own genuinely
independent sync target, "Motorway (accounts)", not a second attempt
bundled into the primary "Motorway" row: _bid() queues both platform
keys for the same star, each gets its own real pending/synced/error
state in the SAME watchlist_sync table (see db.py), so a login not
yet set up on the second account (or a transient failure there) can
never block or perpetually retry the primary account's own already
successful sync, and vice versa. Skipped entirely (no session opened)
if nothing is pending for it, which is what happens automatically
once that connection is hidden on the Integrations tab, since _bid()
checks the same hidden_connections list before ever queuing it.

Headless throughout, unlike the login dependent passes above: nothing
here needs a person watching, the star action itself already proves
the sites are logged in. The primary Motorway account and Carwow both
use browser.open_reader_context (Motorway falls back to the shared
persistent profile every other Motorway read already uses, so this
must never run alongside anything else that touches it); the accounts
login always opens its own separate persistent profile directly
(browser.ACCOUNTS_PROFILE_DIR, never open_reader_context, which has no
reason to know about a profile that is not the shared default). One
continuous session per platform for the whole pending batch, the same
"never a fresh session per car" lesson already learned the hard way
for Glass's and DealerKit.

Each row's own set_watchlist call verifies with a fresh reload before
reporting success (see motorway.py/carwow.py's own docstrings), so a
genuine mismatch is recorded as a real, visible error via db.
mark_watchlist_synced rather than silently believed. A platform with
nothing pending is skipped entirely, no session opened for no reason.zWatchlist sync: dropped z& row(s) for cars no longer on the listz6Watchlist sync: could not prune against the last run (r˜  Nr   rP   c                 ó.   • [         R                  " U SSS9$ rj  rk  rl  s    r0   rË   Ú%watchlist_sync_pass.<locals>.<lambda>0  s   € ”w×2Ò2°1°jÈ4ÒPrE   zMotorway (accounts)c                 óJ   • [         R                  " U S[         R                  S9$ )NT)r‡   Úprofile_dir)r   rA  ÚACCOUNTS_PROFILE_DIRrl  s    r0   rË   r•  3  s   € ”w×+Ò+¨A¸Ì'×JfÑJfÒgrE   rQ   c                 ó.   • [         R                  " U SSS9$ ro  rk  rl  s    r0   rË   r•  6  s   € ”w×2Ò2°1°hÈÒNrE   zWatchlist sync: z pending on rä  zSyncing z	 cars to z's own shortlist...r•   z: could not open a session, rW   Fz/(\d+)(?:[/?#]|$)r˜   rX   z3No listing url on file to find this car's own page.r9   ÚwatchedTrU  r–  Ú	unstarredú on Úsent)Úgive_uprƒ  rE  r”   z synced on Ú	watchlistrƒ  zNothing waiting to sync)r   rÎ   r¶   r\   rW   Úprune_watchlist_syncr¨   r¦   r   Úset_watchlistr   Úpending_watchlist_syncsrš   r™   r   Úmark_watchlist_syncedrF  rž   rŸ   r    r›   r¤   r+   r¥   rM   rZ   r§   rý  )Úcached_shortÚcached_heldÚ_rejÚ_sdro   ÚactiveÚdroppedrK  Útotal_pendingÚtotal_syncedrs  r|  Úwriterrj  Úsyncedr©   r¯   r‡  r°   r±   r³   s                        r0   Úwatchlist_sync_passr®  ò  sî  € ôZ ‡J‚J„LðZÜ/:«}Ñ,ˆ 4Ø%1Ò%?ÓMÒ%? Ç5Á5Ç9Å9“)�!—%‘%—)”)Ñ%?ˆÐMÜ×)Ò)¨&Ó1ˆÞÜÐ,¨W¨IÐ5[Ð\Ô]ð $%Ð$€MàÙPÜ×#Ñ#ð%ð #ÙgÜ×#Ñ#ð%ð ÙNÜ×!Ñ!ð#ô	%$Ñ ˆ˜&ô ×,Ò,¨XÓ6ˆÞÙØœ˜W›Ñ%ˆÜÐ ¤ W£ ¨l¸8¸*ÐEÔFÜ�)˜x¬¨G« ~°Y¸x¸jÐH[Ð\Ø”S˜“\ô	#àˆÜÔ !ðÙ˜Q“i�ðØ'*§y§y�s—y‘y ’|°c·l±l³n�Ü'¨Ö0‘F�A�sÜŸ	š	Ð"6¸¿¹ÀÓ8N×8TÐRTÓU�AÞÜ×0Ò0Ø ™J¨°%ØQôSñ !ð>Ù˜t Q§W¡W¨Q£Z´°c¸)±nÓ1EÔFÜ×0Ò0°°U±¸XÀtÔLØ !™˜Ü  3 u¡: ,¨b¸cÀ)¿n±ÐR]Ð0^Ð^bÐckÐblÐmÔnô ˜i¨8°H°:¸RÀÀAÁ¸wÀdÌ3ÈwË<È.ÐXZÐ[^Ð_dÑ[eÐZfÐ)gØ !™e¤S¨£\ö3ñ) 1ð. —	‘	•÷C ðD 	ÑˆÜÐ   ¨¬S°«\¨N¸+ÀhÀZÐP×Qñk	%$ô| ˆK˜¨!Ñ+×?¨|¸aÑ/?Þ;Hˆ|ˆn˜D  ¨wÐ7õ,à*õ,ùòK Nøô ó ZÜÐFÀqÀcÈÐX×YÒYûðZûô0 ó Ü˜˜8˜*Ð$@ÀÀÐDÔEÛ"�CÜ×,Ò,¨S°©Z¸À5Ì#ÈaË&ÖQñ #ã÷ Òûðûô& %ó >ô
 ×0Ò0°°U±¸XÀuÌcÐRSËfÜ9=¼gÀaÈÐQVÓ>WÓ9XòZä  3 u¡: ,¨j¸¸Ð<×=Ò=ûð>ûð —	‘	•ú÷C Öús£   —L
 ªLÁLÁ&.L
 ÅP
ÅL1ÅBO5Ç4A(NÉ9O5ÊP
ÌL
 Ì

L.ÌL)Ì)L.Ì1
NÌ;?N	Í:P
Î	NÎP
Î
O2	ÎAO-	Ï'O5Ï-O2	Ï2O5Ï5PÐP
Ð

P	c                  ó„   •  SSK n [        U SS5      =(       d    S[        U SS5      =(       d    S4$ ! [         a     gf = f)z·The two Dealer OS settings from this Mac's own dealer_config.py,
blank when that file predates them (Mark's copy, or a fresh install),
which reads as "not set up", never as a failure.r   N)rX   rX   ÚDEALER_OS_BASE_URLrX   ÚDEALER_OS_API_KEY)rº  ÚImportErrorrZ   )rº  s    r0   r)  r)  q  sP   € ðÛô �MÐ#7¸Ó<×BÀÜ�MÐ#6¸Ó;×A¸rðCð Cøô ó Ùðús   ‚2 ²
?¾?c                 ó  • SSK Jn  [        R                  " 5         [	        S5      (       d   SnU (       a  [
        R                  " U5      eg[        5       u  pEU(       a  U(       d   SnU (       a  [
        R                  " U5      egU (       a  [        SS5         UR                  XEUS	9nUR                  S5      (       a  SqUR                  S/ 5       H  n[#        SU 3SS9  M     US    SUS    SUS    3n	U (       a  [        SSX–S   US   5        U$ ! UR                   a[  n [        R                  " S
S[        U5      5        O! [         a     Of = fU (       a  [        SS[        U5      5        e  SnAgSnAff = f)a   Collect what people did in Dealer OS (stars, hides, unhides, saved
views, display settings, run requests) and apply it here through the
same functions the cockpit's own buttons use (2026-09-02, "the way
back"). serve.py runs this every few seconds from its own thread with
the run control hooks; the command line form (python3 daily_run.py
--dealer-os-pull) applies everything except a run request, which needs
the server to start it. Quiet non failure exits: the toggle off, or
Dealer OS not set up on this Mac.r   rK  r%   zEDealer OS is turned off in Settings > Connections. Nothing collected.NzJDealer OS is not set up on this Mac (dealer_config.py). Nothing collected.rî  z(Collecting what was done in Dealer OS...)ÚhooksÚ	dealer_osFÚdealer_os_pullr–  rf  r—  T)Úflushz
 applied, r%  z skipped, cursor ÚcursorÚfetched)rô  r  r   rÎ   rc  r   rM  r)  r™   Úpull_and_applyr6  rE  rF  r¦   rý  r+   r  r¨   )
r•  r´  r  ÚmsgrŒ  r  rÌ  rK  rÊ  r  s
             r0   ró  ró  }  su  € õ (Ü‡J‚J„LÜÐ 0×1Ñ1ØUˆÞÜ—/’/ #Ó&Ð&ØÜ)Ó+Ñ€HÞž7ØZˆÞÜ—/’/ #Ó&Ð&ØÞÜ�)ÐGÔHð
Ø ×/Ñ/°ÈÐ/ÐOˆð ‡{�{�9×Ñð ˆØ—‘˜G RÖ(ˆô 	�˜D˜6Ð"¨$Ô/ñ )ð �iÑ Ð! ¨G°IÑ,>Ð+?Ð?PÐQXÐYaÑQbÐPcÐd€DÞÜÐ  $¨°iÑ.@À'È)ÑBTÔUØ€Nøð) ×+Ñ+ó ð	Ü×!Ò! +¨u´c¸!³fÕ=øÜó 	Ùð	úæÜÐ$ e¬S°«VÔ4ØÜûðús6   ÂD ÄFÄ)!EÅ
E>Å
EÅE>ÅEÅ!E>Å>Fc           
      óä  • U  Vs/ s H  oR                   PM     nn[        R                  " S U 5       5      n[        U Vs1 s H˜  oDR                  (       d  M  [        R
                  " UR                  UR                  5      (       d  MG  UR                  UR                  UR                  5      [        R
                  " UR                  UR                  5      4iMš     sn5      $ s  snf s  snf )zƒThe (make, family) pairs the Model filter is built from, exactly as
render_page computes them for the cockpit's own MODEL_FAMILIES.c              3   ó8   #   • U  H  oR                   v •  M     g 7frŸ  )r  rÚ  s     r0   rX  Ú"_model_families.<locals>.<genexpr>²  s   é € Ð"8²4¨a§6¦6²4ùré  )r\   r   Ú_canon_make_mapr@  r  Ú_model_familyr  r+   )rÞ   ro   rû  ÚcanonrÝ   s        r0   Ú_model_familiesrÂ  ®  s¹   € ñ 'Ó'š;�a�EŒE™;€DÐ'Ü×"Ò"Ñ"8±4Ó"8Ó8€EÜÙ óVÚ �q§F¥Fó VÜ/5×/CÒ/CÀAÇFÁFÈAÏGÉG×/Tó V�E—I‘I˜aŸf™f a§f¡fÓ-¬v×/CÒ/CÀAÇFÁFÈAÏGÉGÓ/TÓUÙ ñVó Wð Wùò (ùòVs   …C(ÁC-Á.C-ÂAC-c                  ó*  •  SSK n U R                  / SQSSS[        R                  R	                  [        R                  R                  [        5      5      S9nUR                  S:X  a  UR                  R                  5       $ S$ ! [         a     gf = f)zAThe release this Mac is on, from git's own tag, or "" if unknown.r   N)ÚgitÚdescribez--tagsz
--abbrev=0TrQ  r¤  rX   )r©  r¬  rì   rí   r9  rª  r«  r²  r¯  r  r¦   )r©  r,  s     r0   Ú_bidbrain_versionrÆ  ·  s|   € ðÛØ�n‰nÒHØ,0°tÀQÜ!#§¡§¡´·±·±ÄÓ1JÓ!Kð ð Mˆð &)§^¡^°qÓ%8ˆs�z‰z×ÑÓ!Ð@¸bÐ@øÜó Ùðús   ‚B B ÂB Â
BÂBc                  óÔ  • SSK n SSKn/ n[        U R                  [        R                  R                  S5      5      5       Hÿ  n [        US5       nUR                  U5      nSSS5        WR                  S5      n[        U[        5      (       d  MR  UR                  UR                  S[        R                  R                  U5      5      [        UR                  SS5      5      S S	[        UR                  S
S5      5      S 3UR                  S5      [        UR                  S5      =(       d    / 5      S.5        GM     U$ ! , (       d  f       Né= f! [         a     GM$  f = f)z‚The Mac's own timed jobs, read from its launchd files (the Mac's
scheduler), for Dealer OS to show read only: what runs, and when.r   Nz+~/Library/LaunchAgents/com.bidbrain.*.plistÚrbÚStartCalendarIntervalÚLabelÚHourr  r¡  ÚMinuteÚDayÚProgramArguments)Újobr=   rC   Úcommand)ÚglobÚplistlibr@  rì   rí   Ú
expanduserry   r€   r¦   r+   rO  rò  rH  Úbasenamer  Ú_schedule_command)rÑ  rÒ  r,  r}   ÚfhÚplÚcals          r0   Ú_run_schedulerÙ  Ã  s$  € ó ÛØ
€CÜ�D—I‘IœbŸg™g×0Ñ0Ð1^Ó_Ó`Öaˆð	Ü�a˜” "Ø—]‘] 2Ó&�÷ ð �f‰fÐ,Ó-ˆÜ˜#œt×$Ñ$ÙØ�
‰
˜2Ÿ6™6 '¬2¯7©7×+;Ñ+;¸AÓ+>Ó?Ü" 3§7¡7¨6°1Ó#5Ó6°sÐ;¸1¼SÀÇÁÈÐSTÓAUÓ=VÐWZÐ<[Ð\ØŸ7™7 5›>Ô6GÈÏÉÐOaÓHb×HhÐfhÓ6iñk÷ 	lñ bð €J÷ •ûäó 	Ûð	ús0   ÁEÁEÁ$EÅ
E	ÅEÅEÅ
E'Å&E'c                 óÜ   • U  Vs/ s H:  n[         R                  R                  U5      R                  S5      (       a  M8  UPM<     nnSR	                  S [        U5       5       5      $ s  snf )zÉ'daily_run.py --under-offer' from a job's ProgramArguments: any
python interpreter dropped whatever its version (a python3.14 path was
slipping through before, 2026-09-07), the script by its name only.Úpythonr  c              3   ót   #   • U  H.  u  pUS :X  a  [         R                  R                  U5      OUv •  M0     g7f)r   N)rì   rí   rÔ  )rW  r±   ro   s      r0   rX  Ú$_schedule_command.<locals>.<genexpr>Ý  s,   é € ÐUÂ_¹T¸Q¨1°«6”B—G‘G×$Ñ$ QÔ'°qÔ8Â_ùs   ‚68)rì   rí   rÔ  rn   rI  r    )Úprogram_argumentsro   Úargss      r0   rÕ  rÕ  Ø  sU   € ñ )ÓYÒ(�!´·±×0@Ñ0@ÀÓ0C×0NÑ0NÈx×0X�AÑ(€DÐYØ�8‰8ÑUÄYÈtÄ_ÓUÓUÐUùò Zs   …7A)Á A)c                 óô  • SSK Jn  [        R                  " 5         [	        S5      (       d+  Sn[        U5        U(       a  [        R                  " U5      eg[        5       u  p‰U(       a  U	(       d+  Sn[        U5        U(       a  [        R                  " U5      egU c   [        5       u  ppBU=(       d    / nUc   [        5       S	   n[        R                  " 5       n
U=(       d    /  Vs/ s Hf  n[        R                  " UR                  R                   5      (       d  M4  [        R                  " UR                  R                   5      U
;  d  Md  UPMh     nnUR#                  S
 S9  [%        XU5      u  ppÞn['        U 5      ['        U5      -   n[)        SSU S35         SSKn[-        USS5      =(       d    Sn[1        [3        5       5      n[        R4                  " 5       US'   UR7                  UXþ[9        X-   5      [;        5       [3        5       R=                  S5      [?        5       [A        5       S9nSSK J!n   URE                  U5      n SSK$nSSK J%n  [3        5       nURM                  5       n URO                  UX‰5      nUR=                  S5      (       d  UR=                  S5      (       a>  [        SUS    SUR=                  SS5       SUS    SUS    3RQ                  5       5        URS                  URU                  U5      [        RV                  " 5       URY                  5       0 S" Vs0 s H  nUUR=                  U5      _M     snES#[        RZ                  " 5       0E[        R\                  " 5       UR=                  S$5      =(       d    / [        R^                  " 5       S%9n SSK$nSS'K J0n   [1        U=(       d    0 5      nURc                  URe                  0 URg                  5       ES#[        RZ                  " 5       0E[        Rh                  " 5       U Rj                   V!s/ s H  n!U!R=                  S(5      PM     sn![m        5       [        R4                  " 5       URn                  URp                  S)95        URs                  XUUUUUUUS,9	n" URu                  U"X‰5      n#['        U 5       S/['        U5       S0U 3nUR~                  (       a&  US1UR~                  S2-   S3UR€                  S2-   S43-  n[ƒ        U#[0        5      (       a  U#R=                  S55      OSn$[ƒ        U$[„        5      (       a  U$U:w  a	  US6U$ S73-  n[ƒ        U#[0        5      (       a  U#R=                  S85      OSn%U%(       a  US9U% 3-  n[        S:U% 35        [        S;U S<35         [        Rx                  " S.S=S5        [}        SS=UUU5        g=! [        [        [        4 a-    Sn[        U5        U(       a  [        R                  " U5      e gf = f! [        [        [        4 a    / n GN^f = fs  snf ! [.         a    Sn GNƒf = f! [F         a  n[        SU S35        Sn SnAGNSnAff = f! [F         a  n[        S U S!35         SnAGN›SnAff = fs  snf ! [F         a  n[        S&U S35        Sn SnAGNSnAff = fs  sn!f ! [F         a  n[        S*U S+35         SnAGNbSnAff = f! URv                   ai  n[        S-U 35         [        Rx                  " S.S[{        U5      5        O! [F         a     Of = f[}        SS[{        U5      5        U(       a  e  SnAgSnAff = f! [F         a     GN¾f = f)>a>  Send the finished list into Dealer OS (Steven 2026-09-01, the first
real step of "get BidBrain inside Dealer OS"). Two ways in: run()
calls this itself right after its own final page write, so Dealer OS
is up to date within moments of every run with nobody pressing
anything, and the Run modal's own button (python3 daily_run.py
--dealer-os-push) re sends the last saved run on demand.

Never a browser, never the shared profile: one HTTPS POST from
bidbrain.dealer_os_push. What crosses the line is only what BidBrain
has already decided (the shortlist and the held cars, each with its
price and its reasons), never raw listings and never the buying rules
themselves, so the Mac stays the single source of truth for buying
decisions and Dealer OS only ever displays them.

Three quiet exits that are NOT failures: the toggle is off in Settings
> Integrations, or the two settings in dealer_config.py are blank
(Dealer OS simply not set up on this Mac, true of Mark's copy until he
adds them), or there is no saved run to send. A push that genuinely
fails records its own run_status ("dealer_os_push") and site_health
("dealer_os"), so the nav bar light and the Integrations tab both
show it, and when called from inside run() it is swallowed there:
BidBrain's own list is written and correct whether or not Dealer OS
could take a copy of it.r   )r%   r%   zEDealer OS push is turned off in Settings > Connections. Nothing sent.FzqDealer OS is not set up on this Mac (DEALER_OS_BASE_URL and DEALER_OS_API_KEY in dealer_config.py). Nothing sent.Nz5No saved run to send to Dealer OS yet. Run now first.r  c                 óp   • U R                   R                  S L U R                   R                  =(       d    S4$ rÝ  rÉ   rÊ   s    r0   rË   Ú%dealer_os_push_pass.<locals>.<lambda>   s#   €  !§%¡%§-¡-°4Ð"7¸¿¹¿¹×9KÈ!Ñ!LrE   rÍ   ræ  r¨  z cars to Dealer OS...r¢  rX   Ú_updated_atr~  )rƒ  rÌ  Úmodel_familiesrÖ  r~  rÓ  Úbidbrain_versionrK  z0Dealer OS push: could not build the state echo (z), sending the list alone.rI  r�  rÊ  zDealer OS documents: z sent (Ú	downsizedz downsized), rv  z	 failed. z+Dealer OS documents: could not send files (ra   )Úpurchase_columnsÚpurchase_flowÚpurchase_viewsÚpurchase_dropdownsÚlost_bids_views)rW  Úunder_offerÚ	lost_bidsÚpurchase_settingsÚpurchase_eventsrë  r"   z>Dealer OS push: could not build the purchase and bid records (rè  rú   )Ú	fee_bandsÚbanned_engine_namesÚrun_scheduleÚ
updated_atÚeditable_sectionsÚbuying_rule_keysz4Dealer OS push: could not build the settings block (z), sending without it.)r°   r‘   rè  r  Ú	held_backzDealer OS push failed: rµ  z shortlisted and z held sent for rE  rŽ  z
 KB sent (z KB uncompressed)Úreceivedz (Dealer OS reports z
 received)Úwarningz. Warning: zDealer OS warning: zDealer OS push: rG  T)Crô  r%   r   rÎ   rc  r¨   r   rM  r)  r¶   rï   rw  ÚKeyErrorr±  r   r\   rW   rÖ   rÎ  rš   r™   rº  rZ   r²  rò  r€  Úsettings_updated_atÚpage_recordrÂ  rá  r+   rà  rÆ  r  Úlocal_stater¦   r°  r  Ú_purchases_with_biasÚsync_documentsr  Úrecords_blockrS  Úlist_under_offerÚ_lost_bids_with_biasr³  Úall_purchase_eventsÚdealerkit_stock_listrã   rô  Úmac_settings_blockÚ_merged_settingsÚget_fee_bandsÚBANNED_ENGINESrÙ  ÚSETTINGS_SECTIONSÚBUYING_RULE_KEYSÚbuild_payloadÚpushÚDealerOsPushErrorrE  rF  rý  ÚLAST_WIRE_BYTESÚLAST_BODY_BYTESrO  r  )&rº   r»   rN   r•  r¼   rè  r%   r»  rŒ  r  Ú
suppressedro   rö  Ú_bidsetrÌ  rƒ  r  rº  Údealer_nameÚpage_settingsr°   r  r‘   rK  r½  r  Ús_allrW  Úuprp   r  Ú_serve2Ú_pricingr   ÚpayloadÚreplyr  rø  s&                                         r0   rü  rü  à  s  € õ2 (Ü‡J‚J„LÜÐ 0×1Ñ1ØUˆÜˆcŒ
ÞÜ—/’/ #Ó&Ð&ØÜ)Ó+Ñ€HÞž7ðGˆäˆcŒ
ÞÜ—/’/ #Ó&Ð&ØØÑð	Ü3>³=Ñ0ˆI˜Xð �:�2€DØÑð	Ü"“} QÑ'ˆHô ×#Ò#Ó%€JØ%Ÿ^¨˜^ó [š^�qÜ—L’L §¡§¡×+ó Ü02·²¸Q¿U¹U¿Y¹YÓ0GÈzÑ0Y÷ ™^€Ið [à‡N�NÑL€NÑMô 2;¸9ÈIÓ1VÑ.€I�W wÜˆI‹œ˜T›Ñ"€AÜˆi˜8 A 3Ð&;Ð<Ô=ðÛÜ˜m¨]¸BÓ?×EÀ2ˆô œ›Ó)€MÜ#%×#9Ò#9Ó#;€M�-Ñ Ø×%Ñ%Ø˜wÜ& yÑ'7Ó8Ü+Ó-Ü(›?×.Ñ.Ð/CÓDÜ"“_Ô7HÓ7Jð &ð L€Dõ (ðØ×*Ñ*¨9Ó5ˆðÛÝ,Ü“ˆØ×/Ñ/Ó1ˆ	ð	GØ ×/Ñ/°	¸8ÓMˆBØ�v‰v�f�~‰~ §¡¨§¡ÜÐ-¨b°©j¨\¸ÀÇÁÈÐUVÓAWÐ@XÐXeÐfhÐiqÑfrÐesÐs|Ð}ð  AGñ  ~Hð  }Ið  J÷  Pñ  Pó  Rô  Sð !×.Ñ.Ø%×0Ñ0°Ó;Ü×+Ò+Ó-Ø×1Ñ1Ó3ðRÑ;rÓ!sÒ;r°a ! U§Y¡Y¨q£\¢/Ñ;rÑ!sð RØ3´R×5NÒ5NÓ5PñRä×2Ò2Ó4Ø!ŸI™IÐ&7Ó8×>¸BÜ×3Ò3Ó5ð /ð 
7ˆð 
`ÛÝ0Ü�w—} "Ó%ˆØ�‰�~×8Ñ8Ø]ˆw×'Ñ'Ó)Ð]Ð+?Ä×AZÒAZÓA\Ñ]Ôik×iyÒiyÓi{Ø8@×8OÒ8OÓ PÒ8O°1 §¡ v¦Ñ8OÑ PÜ&›´R×5KÒ5KÓ5MØ%×7Ñ7È'×JbÑJbð	 9ð dô 	eð ×*Ñ*¨9¸IÀ{Ø04¸EÈ7Ð\cØ5>ð +ð @€GðØ×#Ñ# G¨XÓ?ˆô �Y“Ð Ð 1´#°d³)°¸OÈIÈ;ÐW€Gð
 ×%×%Ø�b˜×7Ñ7¸4Ñ?Ð@ð AØ&×6Ñ6¸$Ñ>Ð?Ð?PðRñ 	Sˆä#-¨e´T×#:Ñ#:ˆ%�)‰)�JÔ
À€CÜ�#”s×Ñ  q£ð 	Ð)¨#¨¨jÐ9Ñ9ˆô '1°¼×&=Ñ&=ˆe�i‰i˜	Ô"À4€GÞØ�[  	Ð*Ñ*ˆÜÐ# G 9Ð-Ô.Ü	Ð˜W˜I QÐ
'Ô(ðÜ
×Ò˜k¨4°Ô4ô Ð˜d G¨Q°Ô2ØøôM œ¤XÐ.ó 	ØIˆCÜ�#ŒJÞÜ—o’o cÓ*Ð*Ùð	ûô œ¤XÐ.ó 	Ø‹Hð	üò[øô ó Ø‹ðûô ó ÜÐ@ÀÀÐC]Ð^Ô_ØŽûðûô$ ó 	GÜÐ?À¸sÀ"ÐE×FÒFûð	Güò "tøô
 ó ÜÐNÈqÈcÐQkÐlÔmØŽûðüò !Qøô ó `ÜÐDÀQÀCÐG]Ð^×_Ò_ûð`ûð ×+Ñ+ó 	ÜÐ'¨ sÐ+Ô,ð	Ü×!Ò! +¨u´c¸!³fÕ=øÜó 	Ùð	úäÐ  %¬¨Q«Ô0ÞØÜûð	ûôH ó Úðús*  Â W Â<X Ã+3X+Ä",X+ÅX+Æ"X0 È=Y É$Z É4A<Y, Ë0AZ Ì3ZÍAZ Î.A>[ Ð,[Ñ<[ Ò[- Ö ]) ×?X
Ø	X
ØX(Ø'X(Ø0Y Ø?Y Ù
Y)ÙY$Ù$Y)Ù,
ZÙ6ZÚZ ÚZÚZ Ú
Z>Ú"Z9Ú9Z>Û[ Û
[*Û[%Û%[*Û-]&Û=]!Ü!\.Ü-]!Ü.
\;Ü8]!Ü:\;Ü;!]!Ý!]&Ý)
]7Ý6]7c            
      ó   • [         R                  " 5         S/ pSS 4SS 44 Hš  u  p#[        SSU S35        [        5        n U" U5      n SSS5        [         R                  " WU5        [         R                  " UR                  5        S
3SS5        U [        U5      -  n [        U S[        U5       S35        Mœ     [        SU(       + U(       d
  U  S35        gU  SSR                  U5       S35        g! [         ag  n[        U S	U 35        [         R                  " UR                  5        S
3S[        U5      5        UR                  U5         SnASSS5        GMC  SnAff = f! , (       d  f       GN(= f)aH  On demand: read every platform's own real "Bids & offers, Under
offer"-style screen (Mark 2026-08-27: "add a under offer section to
the main nav for cars that are won but bids not accepts this is for
all auctions"), a genuinely earlier stage than anything in the
purchases table, a bid Motorway or Carwow currently shows as leading
or waiting on the seller, not yet a confirmed sale at all. Motorway
proven live (a real 2 car example, HK65 style regs, verified both
directions); Carwow's own reader is built and correct in its
navigation (confirmed against the real page, 0 real cars there right
now) but its own per card field parsing has never been proven against
a real populated example, see carwow.read_under_offer's own docstring.
Auction4Cars has a real equivalent screen too (My Account > Awaiting
Payment) but a genuinely different, payment-instructions style page
layout with no populated example to build a reader against yet, a
real, honest gap, not included here. Headless throughout, the same
already-logged-in sessions every other read in this project uses, no
login window needed.r   rP   c                 ó.   • [         R                  " U 5      $ rŸ  )r   Úread_under_offerrl  s    r0   rË   Ú"under_offer_pass.<locals>.<lambda>ª  s   € ¤8×#<Ò#<¸QÔ#?rE   rQ   c                 ó.   • [         R                  " U 5      $ rŸ  )r   r  rl  s    r0   rË   r  «  s   € ¤×!8Ò!8¸Ô!;rE   rä  r‡  z's own Under offer list...z under offer read FAILED: Ú_underofferFNTrX   rU  z car(s) under offerrì  z under offerz under offer, rE  rs  )r   rÎ   r™   r   r¦   r¨   rE  r  rF  rH  Úrecord_under_offerrš   rý  rI  )rµ  rv  rs  r~  r©   r¾  rK  s          r0   Úunder_offer_passr   •  sm  € ô$ ‡J‚J„LØ�rˆ6àÑ?Ð@ØÑ;Ð<ó>Ñˆô 	�)˜y¨¨
Ð2LÐMÔNÜÔ !ð
Ù˜a“y‘÷ ô 	×Ò˜d HÔ-Ü
×Ò §¡Ó!1Ð 2°+Ð>ÀÀbÔIØ”�T“ÑˆÜ��
˜"œS ›Y˜KÐ':Ð;Ö<ñ'>ô. ˆM˜vœ:Þ*0ˆuˆg�\Ð"õPà�7˜.¨¯©°6Ó):Ð(;Ð;MÐNõPøô! ó Ü˜˜
Ð"<¸Q¸CÐ@ÔAô
 ×%Ò%¨¯©Ó)9Ð(:¸+Ð&FÈÌsÐSTËvÔVØ—‘˜hÔ'Û÷ Òûðú÷ Öús1   ÁE.ÁC:Ã:
E+ÄAE&ÅE.Å&E+Å+E.Å.
E=	c                  ó„
  • SSK n [        R                  " 5         [        S5      (       d  [        R
                  " S5      e[        S5        [        5        n[        R                  S   S   nU S3nU S	3n[        X5      u  pVn U(       dJ  [        R                  " SS
S5        [        SS
S5        [        S5         UR                  5         SSS5        g[        R                  " SS5         UR                  [        R                  " S5      S9  ["        R$                  " U5      n[        S['        U5       S35        UR)                  USSS9  UR+                  S5        ["        R,                  " U5      n	[        S['        U	5       S35        UR                  5         [        R                  S   S    S3n
[/        XS5      u  pVn [        R                  " SUU(       a  SOS5        U(       d2  [        SS
S5        [        S5         UR                  5         SSS5        g UR                  [        R                  " S5      S9  [0        R2                  " U5      n[        S['        U5       S 35        [4        R6                  " X‰U5      n['        US!   5      ['        US"   5      -   nS#u  nnnU(       d_  US$    H"  n[        S%US&    S'US(   S) S*US+   S) 35        M$     [        SSS,S-S-5        [        S.5         UR                  5         SSS5        gSnUS!    Hg  nUS--  n[9        S/S0US&    S1U S2U S33UU5         [0        R:                  " UUS&   US4   US5   US6   5        US--  n[        S7US&    S8US4   S) 35        Mi     US"    H^  nUS--  n[9        S/S;US&    S1U S2U S33UU5         [0        R<                  " UUS&   US<   5        US--  n[        S=US&    S>US<    35        M`     US$    H"  n[        S%US&    S'US(   S) S*US+   S) 35        M$     UR                  5          SSS5        [        SSW S@W SAW SB3W=(       d    S-U=(       d    S-5        [        SCU S@U SAU SD['        WS$   5       SE3	5        g! [          a     GN|f = f! UR                  5         f = f! [          a     GNmf = f! [          a%  nUS--  n[        S9US&    S:U 35         SnAGMÆ  SnAff = f! [          a%  nUS--  n[        S?US&    S:U 35         SnAGMˆ  SnAff = f! UR                  5         f = f! , (       d  f       GN= f)Fa­  On demand (and, once proven, daily scheduled) funding sync: reads
LE Capital's own two real screens, Current Stock (still actively
funded) and Stock History (settled), and makes DealerKit's Finance >
Funding list match, added 2026-08-24 (Mark: "LE captial is the source
of truth as this is the funding platform, i need DK funding to match
the LE portal", then "add a daily automation to check funding status
and add or settle the funding status on DK"). LE Capital is always the
source of truth; DealerKit is never treated as more current.

Each site opens from its own saved session and logs itself back in
with its saved password when that has expired (_open_logged_in, Steven
2026-09-02), so this can be started from any device; a headed window
waiting for a person is the last resort only. A car LE Capital shows as funded that DealerKit does not
have gets added (bidbrain.dealerkit_write.add_funding_record); a car
LE Capital shows as settled that DealerKit still has live gets marked
settled (settle_funding_record). A funded_value disagreement on a car
already live on both sides is only ever reported, never auto
corrected, the same conservative stance already used elsewhere in this
project for a similar mismatch. A car DealerKit cannot add because it
is already marked Sold there (a genuine, documented DealerKit
limitation, not a bug, see CLAUDE.md) is skipped with a clear reason,
not treated as a failure that stops the rest of the batch.
r   Nr$   zQLE Capital funding sync is turned off in Settings > Connections. Nothing changed.zLE Capital funding syncr   rŒ  z/stock/current-stockz/stock/stock-historyFz Login was not completed in time.z+LE Capital login was not completed in time.z8LE Capital login not completed in time. Nothing changed.Trj  z  LE Capital Current Stock: z vehicles fundedrˆ   rQ  rŠ   rS  z  LE Capital Stock History: z vehicles settledr   z/finance/fundingza[title="Add Vehicle"]rX   r�  z*DealerKit login was not completed in time.z7DealerKit login not completed in time. Nothing changed.z  DealerKit funding list: z vehicles currently liveÚto_addÚ	to_settle)r   r   r   Ú
mismatchedz1  funded_value disagreement, not auto corrected: rW   u    LE Capital Â£Úle_capital_valuez.2fu    vs DealerKit Â£Údealerkit_valuezAlready matches LE Capitalr9   z<Nothing to do, DealerKit funding already matches LE Capital.rä  zAdding funding for rÀ   r”   rB  Úfunded_valueÚplan_starts_atÚfunding_expires_atz  added funding: u    Â£z  could not add funding for rU  z	Settling Ú
settled_onz  settled: rœ  z  could not settle z added, z
 settled, z skippedz
LE Capital sync done. z
 skipped, z value disagreement(s) flagged.)r=   r   rÎ   rc  r   rM  r¨   r   r   r]  rŠ  rE  rý  r§   rD  rn  r¦   r   Úread_current_stockrš   r¡   r_  Úread_stock_historyr„  r	   Úread_dk_funding_listr
   Ú	plan_syncr™   Úadd_funding_recordÚsettle_funding_record)rb  r©   r.   Úcurrent_urlÚhistory_urlr¯   r°   rG  Ú
le_currentÚ
le_historyÚfund_urlÚdk_logged_inÚ
dk_fundingÚplanr	  ÚaddedÚsettledr%  r³   rÅ  r\   rK  s                         r0   Úlecapital_funding_passr;  Å  sÇ  € ó0 Ü‡J‚J„LÜÐ 3×4Ñ4Ü�oŠoÐqÓrÐrÜ	Ð
#Ô$ä	Ô	˜aÜ�}‰}˜[Ñ)¨*Ñ5ˆØ˜Ð2Ð3ˆØ˜Ð2Ð3ˆô
  /¨qÓ>Ñˆ�9ð	ÞÜ×%Ò% k°5Ð:\Ô]ÜÐ+¨UÐ4aÔbÜÐPÔQØð �I‰IŒK÷7 
Ð	ô ×!Ò! +¨tÔ4ðØ×!Ñ!¤w×'9Ò'9¸+Ó'FÐ!ÑGô #×5Ò5°dÓ;ˆJÜÐ0´°Z³Ð0AÐAQÐRÔSØ�I‰I�kÐ.@È%ˆIÑPØ×!Ñ! $Ô'Ü"×5Ò5°dÓ;ˆJÜÐ0´°Z³Ð0AÐARÐSÔTà�I‰IŒKä—m‘m KÑ0°Ñ<Ð=Ð=MÐNˆÜ"1°!Ð?WÓ"XÑˆ�<ð4	Ü×!Ò! +¨|Þ)5¡2Ð;côeæÜÐ+¨UÐ4`ÔaÜÐOÔPØð\ �I‰IŒK÷g 
Ð	ðLØ×!Ñ!¤w×'9Ò'9¸+Ó'FÐ!ÑGô )×=Ò=¸dÓCˆJÜÐ.¬s°:«Ð.?Ð?WÐXÔYä×)Ò)¨*À*ÓMˆDÜ˜˜X™Ó'¬#¨d°;Ñ.?Ó*@Ñ@ˆEØ&-Ñ#ˆE�7˜GÞØ˜lÔ+�AÜÐMÈaÐPUÉhÈZð X*Ø*+Ð,>Ñ*?ÀÐ)DÐDTÐUVÐWhÑUiÐjmÐTnðpö qñ ,ô Ð+¨TÐ3OÐQRÐTUÔVÜÐTÔUØð8 �I‰IŒK÷g 
Ð	ðp ˆDØ˜H”~�Ø˜‘	�Ü˜)Ð':¸3¸u¹:¸,ÀbÈÈÈdÐSXÐRYÐYZÐ%[Ð]aÐchÔiðLÜ#×6Ò6Ø˜c %™j¨#¨nÑ*=ØÐ,Ñ-¨sÐ3GÑ/HôJð ˜Q‘J�EÜÐ-¨c°%©j¨\¸¸SÀÑ=PÐQTÐ<UÐVÖWñ &ð ˜KÔ(�Ø˜‘	�Ü˜) y°°U±°¸B¸t¸fÀDÈÈÈqÐ%QÐSWÐY^Ô_ðCÜ#×9Ò9¸$ÀÀEÁ
ÈCÐP\ÑL]Ô^Ø˜q‘L�GÜ˜K¨¨E©
 |°4¸¸LÑ8IÐ7JÐKÖLñ )ð ˜,Ô'�ÜÐIÈ!ÈEÉ(Èð T&Ø&'Ð(:Ñ&;¸CÐ%@Ð@PÐQRÐSdÑQeÐfiÐPjðlö mñ (ð �I‰I�K÷g 
ôj Ð ¨%¨°¸¸	ÀÈGÈ9ÐT\Ð']Ø�J�Q˜Ÿ
 ô$ä	Ð$ U G¨8°G°9¸JÀwÀiÈzÜ��lÑ#Ó$Ð%Ð%DðFõ GøôK ó Úðûð �I‰I�Kûô ó Úðûô4 !ó LØ˜q‘L�GÜÐ8¸¸U¹¸ÀBÀqÀcÐJ×KÓKûðLûô !ó CØ˜q‘L�GÜÐ/°°E±
¨|¸2¸a¸SÐA×BÓBûðCûð �I‰I�Kú÷g 
Ö	úsú   Á/T0Â7RÂ=T0ÃRÃ.#R ÄA>RÆ8T0ÇA TÈ	T0È##R&ÉB4TË;T0Ì*TÌ?AR7Î,TÎ/:S)Ï)/TÐT0Ò 
RÒ
RÒRÒRÒR#Ò#T0Ò&
R4Ò0TÒ3R4Ò4TÒ7
S&ÓS!ÓTÓ!S&Ó&TÓ)
TÓ3TÔTÔTÔTÔT-Ô-T0Ô0
T?c                  óz  • [         R                  " 5       n U (       d  [        S5        g[        [        U 5       S35        U  Hv  n[        SUS   S SUR	                  SS	5      S
 SUR	                  SS	5       35        [        SUR	                  SS	5      =(       d    S SUR	                  SS	5       35        Mx     g)z„Print the cars suppressed because a platform sale was cancelled or voided,
so Steven can audit what is being hidden for that reason.z*No cancelled or voided purchases recorded.Nz3 cars suppressed after a cancelled or voided sale:
r•   rW   Ú10r  rs  rX   Ú9rú   z      status: rk   z(none)z
   noted: Únoted_at)r   Úwalked_away_listr¨   rš   r+   rÈ  s     r0   Úwalked_awayrA  >  s­   € ô ×ÒÓ €DÞÜÐ:Ô;ØÜ	ŒS�‹YˆKÐKÐ
LÔMÛˆÜ��1�U‘8˜B�-˜q §¡ z°"Ó!5°aÐ 8¸¸!¿%¹%ÀÀrÓ:JÐ9KÐLÔMÜ�˜qŸu™u X¨bÓ1×=°XÐ>¸jÈÏÉÈzÐZ\ÓI]ÐH^Ð_Ö`ò rE   c                 óÖ  • [         R                  " U 5      n S[        S'    [        5       u  pp4[        R                  R                  U5      nU Vs/ s H5  n[         R                  " UR                  R                  5      U :X  d  M3  UPM7     nnU(       d  gU Vs/ s H5  n[         R                  " UR                  R                  5      U :w  d  M3  UPM7     nn/ n	U Hí  n[        UR                  U5      n
U
R                  S:X  a  UR                  U
5        M<  U	R                  U
5        U
R                  S:X  aI  U Vs/ s H5  n[         R                  " UR                  R                  5      U :w  d  M3  UPM7     snU
/-   nM¦  U Vs/ s H5  n[         R                  " UR                  R                  5      U :w  d  M3  UPM7     snU
/-   nMï     U	(       d  gUR                  S S9  [         R                   " X-   U-   5        [#        XX„5        [%        XX…U5        ['        S U	 5       5      (       a  SOS	nU  S
U 3$ ! [        [
        [        4 a     gf = fs  snf s  snf s  snf s  snf )aÚ  Apply a release straight away (2026-09-10): the car in the last run's
rejected list is assessed again with its release row, so it moves to
the held list (unpriced, with his reason and the rules it broke as
flags) or, when its valuations are already known, straight to the
shortlist. The cache and pages are rewritten; the caller pushes. The
price comes with the next run, which values it like any other car.
Returns a short note, or None when the car is not in the last run.r'  r"  Nr¼   rº   c                 ó@   • U R                   R                  =(       d    S$ rÝ  rÉ   rÊ   s    r0   rË   Úrelease_now.<locals>.<lambda>k  s   €  §¡§¡×!3°!Ð!3rE   rÍ   c              3   ó>   #   • U  H  oR                   S :H  v •  M     g7f)rº   NrF  rV  s     r0   rX  Úrelease_now.<locals>.<genexpr>o  s   é € ÐFÂ¸1Ÿx™x¨;Ö6Âùs   ‚z!held list, priced on the next runz moved to the )r   r   r%  r¶   rï   rw  rù  r>   r<   r·   r\   rW   rÔ   rk   rH  rÖ   ré  r¸   r¹   r½  )rW   rº   r»   r¼   rN   rI   ro   r$  Úrestrþ  Úfreshrû   r»  s                r0   Úrelease_nowrI  K  sö  € ô �,Š,�sÓ
€CØ„O�DÑðÜ/:«}Ñ,ˆ	˜ô �M‰M×'Ñ'¨	Ó2€EÙ ÓCš�1¤B§L¢L°·±·±Ó$;¸sÑ$B�Q™€EÐCÞØÙÓB’x�!¤2§<¢<°·±·	±	Ó#:¸cÑ#A�A‘x€DÐBØ€EÛˆÜ˜Ÿ™˜uÓ%ˆØ�<‰<˜:Ó%Ø�K‰K˜ÔÙØ�‰�UÔØ�<‰<˜;Ó&Ù$-ÓP¢I˜q´·²¸a¿e¹e¿i¹iÓ1HÈCÑ1OŸ¡IÑPÐTYÐSZÑZŠIá#ÓFšt˜!¤r§|¢|°A·E±E·I±IÓ'>À#Ñ'E—A™tÑFÈ%ÈÑPŠDñ ö ØØ‡N�NÑ3€NÑ4Ü×Ò˜	Ñ(¨4Ñ/Ô0Ü�	 Ô1Ü� $¨yÔ9ÜÑFÁÓF×FÑF‰KÐLo€EØˆU�.  Ð(Ð(øô3 ”Z¤Ð*ó Ùðüò Dùò Cùò QùâFsA   ¡H< Á2IÂIÂ2IÃIÄ>2I!Å4I!Æ2I&Æ=I&È<IÉIc            
      óü  • [         R                  " SS9n U (       d  [        S5        g[        [        U 5       S35        U  H¢  nUR	                  S5      =(       d5    UR	                  SS5       S	UR	                  S
S5       3R                  5       n[        SUS   S S	U 35        [        SUR	                  SS5       35        [        SUR	                  SS5       S35        M¤     [         R                  " SS9nU(       að  [        [        U5       S35        U HÓ  nUR	                  S5      =(       d5    UR	                  SS5       S	UR	                  S
S5       3R                  5       n[        SUS   S S	U 35        [        SUR	                  SS5       35        [        SUR	                  SS5       35        [        SUR	                  SS5       SUR	                  SS5       S35        MÕ     [        S5        g)zˆPrint the cars Steven has hidden with a reason that have not yet been
turned into rules, so the assistant can mine them into pricing.py.T)Úonly_newzNo new hide notes to review.Nz7 cars hidden with a reason, not yet turned into rules:
rú   r  rX   r  r  r•   rW   r=  z      reason: rz  z      hidden: Ú	hidden_atrÇ   zG cars released from a hold with a reason, rules to consider softening:
z      rules: Úrulesz      released: Úreleased_atz by r¯  zyTurn the recurring reasons into rules in bidbrain/pricing.py, then mark them reviewed with db.mark_notes_reviewed([...]).)r   Úhidden_notesr¨   rš   r+   r  Úreleased_notes)r¾  r   rú   Úreleaseds       r0   rO  rO  s  sÓ  € ô �?Š? DÑ)€DÞÜÐ,Ô-ØÜ	ŒS�‹YˆKÐOÐ
PÔQÛˆØ�u‰u�V‹}×Q 1§5¡5¨°Ó#3Ð"4°A°a·e±e¸GÀBÓ6GÐ5HÐ I× OÑ OÓ QˆÜ��1�U‘8˜B�-˜q  Ð'Ô(Ü�˜qŸu™u X¨bÓ1Ð2Ð3Ô4Ü�˜qŸu™u [°Ó4Ð5°RÐ8Ö9ñ	 ô
 × Ò ¨$Ñ/€HÞÜ”�X“�ÐgÐhÔiÛˆAØ—5‘5˜“=×U q§u¡u¨V°BÓ'7Ð&8¸¸!¿%¹%ÀÈÓ:KÐ9LÐ$M×$SÑ$SÓ$UˆDÜ�B�q˜‘x �m 1 T FÐ+Ô,Ü�N 1§5¡5¨°"Ó#5Ð"6Ð7Ô8Ü�M !§%¡%¨°Ó"3Ð!4Ð5Ô6ÜÐ$ Q§U¡U¨=¸Ó%<Ð$=¸TÀ!Ç%Á%ÈÈbÃ/ÐARÐRTÐUÖVñ ô 
ð Cõ DrE   c                  ó  • [         R                  " 5       n U S   (       d  [        S5        g[        SU S    S35        U S   (       a.  [        S5        U S   SS  H  u  p[        S	US
 S	U 35        M     U S   (       a.  [        S5        U S   SS  H  u  p2[        S	US
 S	U 35        M     U S   (       a=  U S   n[        S[        US   5       S[        US   5       S[        US   5       35        U S   (       a%  U S   n[        SUS   S SUS   S SUS   S 35        U S   (       a"  U S   n[        SUS    SUS    SUS    35        U S   (       a4  [        SSR	                  S U S   R                  5        5       5      5        gg)zÒPrint what BidBrain has learned about Steven's taste from the cars he has
starred, make and model first. Learning runs quietly, this is how to see it
building. Surfaced on the cockpit later once it has settled.r	  zBNo shortlisted cars yet, so nothing learned. Star some cars first.NzLearned from z shortlisted cars.
r
  zMakes you star most:r�  r•   Ú3r  z
Models you star most:r  z
Usual price (reserve): r  z to rð   z	, median Úmedianr  zUsual mileage: r3  r  zUsual year: r{  zPlatform split:rE  c              3   ó4   #   • U  H  u  pU S U 3v •  M     g7f)r  NrG   )rW  rp   rq   s      r0   rX  Úhabits.<locals>.<genexpr>¨  s   é € Ð*^ÒB]¹$¸!¨a¨S°°!°­:ÒB]ùs   ‚)r   r·  r¨   r   rI  rm   )ÚprofÚmkr  Úmdr�  s        r0   ÚhabitsrZ  �  s²  € ô × Ò Ó"€DØ��=ÜÐRÔSØÜ	ˆM˜$˜w™-˜Ð(<Ð
=Ô>ØˆI‡ÜÐ$Ô%Ø˜)‘_ S bÓ)‰EˆBÜ�B�q˜�e˜2˜b˜TÐ"Ö#ñ *àˆJ×ÜÐ'Ô(Ø˜*Ñ% c rÓ*‰EˆBÜ�B�q˜�e˜2˜b˜TÐ"Ö#ñ +àˆG‡}Ø�‰MˆÜÐ)¬&°°5±Ó*:Ð);¸4ÄÀqÈÁxÓ@PÐ?QÐQZÔ[aÐbcÐdlÑbmÓ[nÐZoÐpÔqØˆI‡Ø�‰OˆÜ�  %¡¨˜|¨4°°%±¸¨|¸9ÀQÀxÁ[ÐQRÀOÐTÔUØˆF‡|Ø�‰LˆÜ�˜Q˜u™X˜J d¨1¨U©8¨*°I¸aÀ¹k¸]ÐKÔLØˆM×ÜÐ §¡Ñ*^À$À}ÑBU×B[ÑB[ÔB]Ó*^Ó!^Õ_ð rE   Ú__main__z--render-onlyz--reviewz--photos-nowz--photosz--glassr   z--clickdealerr   z--dealerkitr"   z--dealerkit-purchasesr#   z--dealerkit-checkr†  z--dealerkit-stagesr‚  z--dealerkit-sale-pricesr\  z--sales-backfillrL  z--dealerkit-prober¥  z--motorway-proberd  z--lecapital-fundingr$   z--watchlist-syncrŸ  z--deep-readrh  z--under-offerrì  z--dealer-os-pushr%   z--dealer-os-pullr¶  z--habitsz--hidden-notesz--walked-awayz--recheck-detailsz	--repricez--testr9   z--platformsr3  r¬  c                  ó&   • [        [        [        S9$ )N)rÿ  r  )r¬  ÚtlÚplatsrG   rE   r0   rË   rË   ê  s   € ¤#´¼uÒ"ErE   )rY  rŸ  )NrG   )NNFN)rV  )F)FFF)r~  )rÎ  )r[  )r  N)TN)NNNTNF)½Ú__doc__r­  rì   r›   r{   r>   Údataclassesr   r   rù   Úplaywright.sync_apir   rô  r   r   r   r	   r
   r   r   r   rº  Úbidbrain.readersr   r   r   r   r   r   r   r   r   r   Úbidbrain.pricingr   r   r   r   r   r   r   r   r    r  r*   r  r1   r5   r=   ÚSALE_ENDS_ATrD   rJ   rO   r›  rí   rI  r9  rª  r«  ré   rz   ÚPROGRESSÚprogressr™   Úfinishrý  r]   rg   r~   r‚   r´   r½   rÅ   rß   r¸   ró   r¶   r  r%  Ú	TOP_PICKSr;  rB  rP  ro  rp  rl  rt  r{  r�  rˆ  rœ  rÎ  r¹   rê  rì  r  r	  r  r  r  r€  rà  rc  rá  r‚  r  rÕ   r%  r'  r  rÔ   r-  r2  r/  r4  r:  r=  rÏ   rG  rf  rÐ   rÑ   rÒ   rÓ   r¬  r.  r9  rI  rP  re  r{  r„  rŠ  r’  rÆ  r¸  rÜ  r¹  rð  r¬  r  ÚPROBE_WORDSr%  r'  r4  rP  rR  rT  rp  rÅ  rÝ  rí  rï  rõ  rú  rþ  r  r
  r  rD  rZ  rf  r€  r„  r’  r®  r)  ró  rÂ  rÆ  rÙ  rÕ  rü  r   r;  rA  rI  rO  rZ  rN  ÚargvÚguardr]  Úindexr±   rš   r  r^  r  rÍ   s   0r0   Ú<module>rm     s  ðñó& Û 	Û 	Û Û ß :Ý /ç f× fÓ fÛ ÷ H÷  H÷  H÷÷ õ ð( " 4Ð(Ø% tÐ,Ø˜ÐØ# TÐ*ð ! $Ð'ð
 ˜uÐ%ñÐ ò( U€ñ 3@Ó@²-¨3 $šY±-Ñ@Ð ò
?ò$ð. �}Š}˜R Ó$€ð +ô [òò@ð ØØ"Ø%Øñ€ð 	�‰�‰�R—W‘W—_‘_ R§W¡W§_¡_°XÓ%>Ó?ÀÈÓY€Ø�w‰w�|‰|˜BŸG™GŸO™O¨B¯G©G¯O©O¸HÓ,EÓFÈÐP]Ó^€Ø�7‰7�<‰<˜Ÿ™Ÿ™¨¯©¯©¸Ó(AÓBÀFÐL_Ó`€ð �M‰M€	Ø
�+‰+€òRòò0Vò&ôòD6ò!>ôHbò>ô&*ò$#ò&ò$ð< €	ô>ðB 'ô 
òGð6 Ð Ø€ò0ôfô,ð0  hÀØ%°KÈlñÐ ò!ôHò$Z5òzò:"ò*fò;'ò|=ò<	Hò7ð €ò	òSòbò\òYòò0!ð$  RÑ(€ò"òBò-ð  �W‰W�\‰\˜"Ÿ'™'Ÿ/™/¨"¯'©'¯/©/¸(Ó*CÓDÀfÈfÐVoÓp€
Ø—‘—‘˜bŸg™gŸo™o¨b¯g©g¯o©o¸hÓ.GÓHÈ&ÐRkÓl€òò	òò2ò,ò^2ôj*òZ.òb+ò\ò('ôTm[ò`ò&Eô$2òjhSòVEbôPNôbPòDò;`ò|x$òvQô1ôh[%ò|hð€òð€ð @€
ôð& )°¸ô ð&c€ô
ô,òRnòj{pò|@òFnò2kò/ò81òhKôð —'‘'—,‘,˜rŸw™wŸ™¨r¯w©w¯©¸xÓ/HÓIÈ6ÐSaÓb€òô0w,òtS)òl10òhN.òb*+òZ^`òB|,ò~	Cô.òbWò	òò*Vð ^bØ %ôròj-Pò`vGòr
aò%)òPDò4`ð< ˆzÔØ˜#Ÿ(š(Ó"Ù�Ø	�s—x’xÓ	Ù�Ø	˜3Ÿ8š8Ó	#ÙÕØ	�s—x’xÓ	Ù�Ø	�c—h’hÓ	Ø�
‹
�7˜JÕ'Ø	˜CŸHšHÓ	$Ø�
‹
�=Ð"2Õ3Ø	˜#Ÿ(š(Ó	"Ø�
‹
Ð$ nÕ5Ø	  C§H¢HÓ	,Ø�
‹
Ð(Ð*BÕCØ	 §¢Ó	(Ø�
‹
Ð$Ð&:Õ;Ø	 §¢Ó	)Ø�
‹
Ð%Ð'<Õ=Ø	" c§h¢hÓ	.Ø�
‹
Ð*Ð,FÕGØ	˜sŸxšxÓ	'Ø�
‹
Ð#Ð%8Õ9Ø	 §¢Ó	(Ø�
‹
Ð$Ð&:Õ;Ø	˜sŸxšxÓ	'Ø�
‹
Ð#Ð%8Õ9Ø	 #§(¢(Ó	*Ø�
‹
Ð&Ð(>Õ?Ø	˜sŸxšxÓ	'Ø�
‹
�;Ð 3Õ4Ø	˜#Ÿ(š(Ó	"Ø�
‹
�; Õ/Ø	˜CŸHšHÓ	$Ø�
‹
�=Ð"2Õ3Ø	˜sŸxšxÓ	'Ø�
‹
Ð#Ð%8Õ9Ø	˜sŸxšxÓ	'Ø�
‹
Ð#Ð%8Õ9Ø	�s—x’xÓ	Ù�Ø	˜SŸXšXÓ	%Ù�Ø	˜CŸHšHÓ	$Ù�Ø	 §¢Ó	(ÙÕØ	˜ŸšÓ	 Ù�	àˆØ�s—x’xÓØ—’—’˜xÓ(ˆAØ)*¨Q©±°S·X²X³Ó)>‘�S—X’X˜a !™e‘_Ô%ÀAˆBØˆØ˜CŸHšHÓ$Ø—’—’˜}Ó-ˆAØ23°a±%¹#¸c¿hºh»-Ó2G�C—H’H˜Q ™U‘O×)Ò)¨#Ô.ÈTˆEð 	�
‹
�5ÑEÕFð ùòoJ As   Âa(