---
title: "Sääksi Bike: kaksi päivää, kaksi sovellusta ja 1,6 miljardia tokenia"
slug: saaksi-bike-kaksi-paivaa-ja-miljardi-tokenia
summary: "Toukokuussa syntynyt tapahtumasivu muuttui elokuussa live-palveluksi, kioskiksi ja kahden alustan mobiilisovellukseksi. Kävin Claude- ja Codex-historiat läpi: vauhti oli järjetön, mutta kiinnostavinta olivat ne kohdat, joissa oikea puhelin, tuotantopalvelu tai toinen agentti sanoi vastaan."
tags: tekoälyagentit, ohjelmistokehitys, Flutter, Claude, Codex, Sääksi Bike
ai_model: gpt-5.6-sol
published_at: 2026-08-31T05:06:26.162+00:00
canonical_url: https://jarkkolietolahti.com/articles/saaksi-bike-kaksi-paivaa-ja-miljardi-tokenia
markdown_url: https://cemecdwfdvdbvpvowuln.supabase.co/functions/v1/content-md?slug=saaksi-bike-kaksi-paivaa-ja-miljardi-tokenia
---

Toukokuun 10. päivänä pyysin agenttia tekemään verkkosivun maastopyöräilytempaukselle. Ajatus oli vielä yksinkertainen: kymmenen hieman erilaista kierrosta Sääksjärven ympäri, kaupat ja kioskit kartalle, sauna mukaan ja kavereille osoite, josta näkee mitä ollaan tekemässä.

Elokuun 30. päivänä sama järjestelmä koostui 18-sivuisesta sivustosta, Cloudflare Worker -taustapalvelusta, livekartasta, kierroslaskennasta, historiasta, replaysta, näyteikkunakioskista sekä yhdestä Flutter-koodipohjasta syntyneistä Android- ja iOS-sovelluksista. Androidin allekirjoitetun APK:n pystyi jo lataamaan suoraan saaksi.bike-sivulta. iOS-versio 1.0 odotti Applen katselmusta.

Mobiilirepossa oli 41 committia. Ensimmäisestä viimeiseen kului 52 tuntia ja 13 minuuttia.

Tämä ei ollut suunnitelmallinen kahden vuorokauden sprintti. Tämä oli Fast & Furious -elokuva, jossa autot oli korvattu agenteilla, USB-kaapeleilla ja yhdellä Sääksjärven ympäri kiertävällä GPX-jäljellä.

## Ensimmäinen versio syntyi illassa, varsinainen järjestelmä elokuussa

Alkuperäinen sivupyyntö tuli 10.5.2026 kello 22.38 Suomen aikaa. Ensimmäinen site-commit syntyi samana iltana kello 23.47. Se oli staattinen tapahtumasivu, ei vielä oma palvelu.

Varsinainen kiihtyminen alkoi elokuussa. Elokuun 11. päivä sivu sai aikataulun, oikean perusreitin, ilmoittautumisen, livekartan ja myöhemmin oman `saaksi.bike`-domainin. Tapahtumapäivän jälkeen sivustoa ei laitettu arkistoon. Se alkoi elää taaksepäin: replay, valokuvat kartalla, kierrosajat, kisastudio, henkilö- ja vuosikertomukset, 2027-suunnitelma.

Git-historia tekee vauhdin näkyväksi. `site/`-hakemistolla oli tarkistushetkellä 171 committia, joista 170 oli syntynyt 11.–30. elokuuta. Pelkästään 27.–30. elokuuta niitä tuli 53.

Agenttihistoria lisää commit-listaan sen osuuden, jota Git ei näytä. Yhdessä ikkunassa Claude rakensi vuosihistoriaa ja navigaatiota. Toisessa Codex katselmoi live-palvelua. Kolmas agentti simuloi pyöräilijää. Agentit lähettivät toisilleen tilapäisiä viestejä, pyysivät taukoa branchin vaihdon ajaksi ja yrittivät pitää yhteisen checkoutin kasassa.

Se toimi, kunnes ei toiminut.

## Ensimmäinen törmäys oli oikea tietoturvavirhe

Elokuun 27. päivän katselmuksessa Codex löysi live-palvelusta viisi ongelmaa. Vakavin oli session haltuunotto: julkisessa live-datassa näkyneen Beacon-tunnisteen pystyi lähettämään takaisin palvelulle ilman omistajuustokenia, jolloin hyökkääjä saattoi saada session tokenin ja lopettaa toisen ajon.

Samassa katselmuksessa löytyi tuoreen käyttäjän rikkinäinen share target, kolminkertainen Beacon-haku joka saattoi ylittää Cloudflaren ilmaistason 50 alipyynnön rajan, ensimmäistä cron-ajoa edeltävästi katoava historia sekä R2-varmuuskopio, jota ei oikeasti luettu takaisin.

Tämä oli ensimmäinen tärkeä jarrutus. Sivusto näytti toimivalta ja JavaScript läpäisi syntaksitarkistukset, mutta tuotantologiikassa oli vikoja, joita selainkuva ei paljasta. Löydökset muutettiin tiketeiksi, korjattiin, testattiin previewssa ja vietiin tuotantoon erillisinä muutoksina.

Samaan aikaan yhteinen branch ehti napata mukaansa aivan väärän Android-tiedoston. Claude sai pyynnön commitoida "kaikki muutokset nice grouped", ja repoon päätyi toiseen projektiin kuulunut `GeminiClient.kt`, vieläpä kovakoodatulla API-avaimella. Claude ei ollut kirjoittanut tiedostoa, mutta oli commitoinut sen lukematta sisältöä.

Avain peruttiin, tiedosto myöhemmin pelastettiin ilman salaisuutta ja työskentelymalli vaihdettiin erillisiin worktree-hakemistoihin. Tässä projektissa agenttien nopeus ei ensin rikkonut koodia vaan omistajuuden: kukaan ei enää tiennyt varmasti, mikä muutos oli kenen ja mihin se kuului.

## Kello 7.43 kysyin vaihtoehtoa Beaconille, kello 8.14 mobiilirepo oli olemassa

Elokuun 28. päivänä kello 7.43 kysyin Codexilta, millä muilla tavoilla pyöräilijän live-GPS:n saisi sivulle kuin Strava Beaconilla. Kuusi minuuttia myöhemmin kysyin, kuinka iso työ iOS- ja Android-sovellusten vieminen sovelluskauppoihin olisi. Kello 7.56 pyysin suunnitelman, tiketit ja omat yksityiset GitHub-repot.

Ensimmäinen `saaksi-bike-mobile`-commit syntyi kello 8.14. Flutter-runko oli repossa kello 8.42. Kello 10.08 historiassa ovat peräkkäin yhteinen seurantasovellus, Android-integraatio, iOS-integraatio ja molempien merge-commitit.

Arkkitehtuurin ydin oli tarkoituksella pieni. Yksi Flutter-koodipohja, ei käyttäjätiliä, yksi anonyymi ajosessio kerrallaan. Cloudflare Turnstile varmisti että aloittaja oli ihminen. Sovellus lähetti sijainnin olemassa olevalle live-palvelulle ja näytti matkan, reitin ja kierrokset. Android ja iOS lisäsivät omat deep linkinsä, taustaseurantansa ja älykkään lähtöehdotuksen.

Tässä kohtaa agentin ruudulla kaikki eteni epäilyttävän hyvin. Sitten kytkin OnePlus 15:n USB-kaapelilla Maciin.

## Oikea puhelin muutti suunnitelman ohjelmistoksi

Androidille tarvittiin toistettava testi, jossa sovellus ei saanut vain yhtä sijaintipistettä vaan kokonaisen ajon. Tavallinen Androidin test-provider-polku pysähtyi OxygenOS:n suojaukseen: shell ei saanut mock location -oikeutta ilman OnePlus-tiliin sidottua "USB debugging (Security settings)" -valintaa.

Ratkaisu löytyi Appium Settings -sovelluksesta. Puhelimessa valittiin se käsin mock location -sovellukseksi, ja repoon tehtiin `gpx-replay.py`, joka syötti oikean Sääksi-ajon pisteitä GPS-, network- ja fused-providerien kautta. Kahden tunnin reitti voitiin puristaa testissä alle minuuttiin säilyttäen alkuperäinen aikajana ja realistinen nopeusdata.

Täysi reitti paljasti, että julkiseen palveluun lähetetty harva pistejoukko aliarvioi matkaa mutkaisella polulla. Korjaus ei mahtunut sovelluksen pinnalle. Traccarin seuranta-SDK:sta tehtiin oma forkki, johon lisättiin yksityisyyttä säilyttävä paikallinen matkamittari ja kellon palautumisen kestävä logiikka. Sama testi löysi SQLite-versioiden 3 ja 4 välisen downgrade-kaatumisen, jos laitteelle asennettiin väärää SDK-varianttia käyttävä buildi.

Yhden vihreän nappitestin sijasta hyväksyntä tarkoitti nyt ketjua: käynnistä ajo, syötä GPX, lukitse näyttö, katkaise verkko, palauta verkko, varmista jonon purkautuminen, lopeta ajo, poista testidata. Oikea laite oli se kohta, jossa "appi valmis" lakkasi tarkoittamasta "Flutter-testit menivät läpi".

## Codex jäi valkoiseen ruutuun, Claude jatkoi siitä

iPhonen ensimmäinen automaatiokierros onnistui 29. elokuuta. iPhone SE:n saavutettavuuspuuta voitiin lukea Appiumilla, preview-Turnstile meni läpi, lukitun näytön taustapiste saapui palvelulle ja verkkokatkon aikana SQLiteen jääneet pisteet purkautuivat verkon palattua.

Tuotannossa sama sovellus pysähtyi valkoiseen WebView-ruutuun: "Tarkistus ei latautunut." Codex tutki canonical-URL:ää, widgetin hostname-asetuksia, Xcoden luottamusta ja Wrangler-lokeja. Olennaisin havainto oli negatiivinen: konfiguraatio latautui, mutta `/v1/mobile/sessions`-pyyntöä ei koskaan tullut. Preview oli antanut väärän turvallisuuden tunteen, koska se käytti Turnstilen aina läpäisevää testiavainta.

Kello 23.53 pyysin Claudea auttamaan ja huomioimaan Codexin työn. Claude otti käyttöön Xcoden, oikean iPhonen, Appiumin ja Cloudflare-lokit. Lopullinen korjaus salli Turnstilen challenge-kehysten käyttämät `about:blank`- ja `srcdoc`-navigoinnit iOS-WebViewssa. Tuotantotesti meni läpi: Turnstile, sessio, lukitun näytön piste, lopetus ja poisto.

Samassa yhtäjaksoisessa yövuorossa buildi `1.0.0 (4)` validoitiin, ladattiin App Store Connectiin, asennettiin TestFlightista takaisin samaan iPhoneen ja testattiin uudelleen tuotantoa vasten. Lopuksi Claude painoi käyttäjän luvalla Add for Review ja Submit for Review. Tila vaihtui muotoon `Waiting for Review`.

Android kulki eri maaliin. Google Play -tilin varmennusta odotellessa release allekirjoitettiin paikallisella upload-keystorella ja 19 megatavun arm64-APK jaettiin suoraan saaksi.bike-sivulta. Elokuun 30. päivänä korjattiin vielä offline-tilassa aloitetun session upload-toipuminen, lisättiin palvelinohjattu satunnaisnimi ja nostettiin build-numero viiteen.

## Historiassa kulki noin 1,64 miljardia tokenia

Halusin lopuksi tietää, mitä tällainen kehitys tarkoitti tokenimääränä. Laskin Claude-historiasta 14 Sääksi-työhön rajattua sessiota ja poistin saman viestin toistuvat lokirivit viesti-ID:n perusteella. Codexista otin alkuperäisen toukokuun site-jakson sekä elokuun site-, kioski- ja mobiilisessiot. Sekasessioiden takia luku on arvio, ei kirjanpidon totuus.

Tulos oli noin 1,64 miljardia tokenia:

- Claude: noin 1,072 miljardia
- Codex: noin 569 miljoonaa

Luku kuulostaa vielä järjettömämmältä kuin se on. Noin 98 prosenttia liikenteestä oli välimuistista luettua aiempaa kontekstia. Agentti ei kirjoittanut 1,6 miljardia tokenia uutta koodia; se luki samat ohjeet, keskustelun, työkalutulokset ja projektikontekstin uudelleen tuhansissa mallikutsuissa. Ulostulotokenien yhteismäärä oli noin 3,7 miljoonaa.

Hinnoittelin historian nykyisillä julkisilla API-hinnoilla: Clauden Fable 5- ja Opus 5 -hinnat [Anthropicin taulukosta](https://platform.claude.com/docs/en/about-claude/pricing) ja Codex-liikenteen GPT-5.6 Solin vertailuhinnalla [OpenAI:n mallitaulukosta](https://developers.openai.com/api/docs/models). Välimuistit hinnoiteltiin erikseen, ei täyteen input-hintaan.

API-vastaava kustannus olisi noin 1 667 dollaria. [Euroopan keskuspankin 28.8.2026 viitekurssilla](https://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.pl.html), 1 euro = 1,1643 dollaria, se on noin 1 431 euroa.

Se ei ole lasku. Työ ajettiin Claude- ja Codex-tilausten kautta, eikä historiasta voi päätellä kuukausimaksujen todellista kohdistusta tälle projektille. API-vastaava luku kertoo mittakaavan: mitä samojen mallikutsujen listaostaminen olisi maksanut.

## Nopeus syntyi rinnakkaisuudesta, valmius vastuksesta

Kahdessa päivässä voi agenttien avulla tehdä hämmästyttävän paljon. Tässä tapauksessa yksi koodipohja muuttui kahdeksi oikealla laitteella toimivaksi sovellukseksi, Android-jakeluksi ja App Store -lähetykseksi samalla kun verkkopalvelu eli mukana.

Mutta historia ei tue kertomusta, jossa agentti sai yhden promptin ja sylkäisi valmiin tuotteen. Vauhti syntyi rinnakkaisuudesta. Laatu syntyi vastuksesta.

Vastus oli tietoturvakatselmus, joka löysi session haltuunoton. Se oli kovakoodattu avain väärässä branchissa. Se oli OxygenOS:n estämä mock location, mutkittelevan polun rikkoma matkamittari, SQLite-migraatio, iPhonen luottamusketju ja Turnstile-kehys, jota preview ei koskaan pakottanut kohtaamaan. Se oli myös ihminen, joka kytki puhelimen kaapeliin, hyväksyi luvat, katsoi valkoista ruutua ja sanoi: ei tämä vielä toimi.

Ensimmäinen commit syntyi nopeasti. Valmis sovellus syntyi vasta, kun todellisuus ehti mukaan.

