Short status and the working backlog. Log entries before 2026-10-01 are in git history;
BASE_PLAN.md holds the long-term architecture.
One command (src/obehy/cli.py) resolves the GVD year, validates or refreshes the shared OSM
snapshot, builds JrUtil once, and runs these stages. Production runs daily on GitHub Actions
(.github/workflows/build.yml, BASE_PLAN.md section 15) and publishes each run as a
build-<run-id> GitHub release; green since 2026-10-08.
- fetch-sources — VLD and dráhy CIS JŘ archives, CZPTT (inventory, KADR, SR70), PID and IDS
JMK GTFS, all before JrUtil is built; three attempts per source,
sources/fetch-log.json. - national-jdf — the fetched VLD and dráhy archives →
fix-jdf(stop matching, coordinate estimation,regional-adjacentinternational policy) →merge-jdf(name-based stop reconciliation) →jdf-to-bundle→validate-package. - regional-overlay — overlay the fetched PID and IDS JMK GTFS onto
the national JDF package in one
regional-gtfs-overlaypass. - filtered-jdf — a plain GTFS derived from the pre-overlay national JDF, filtered like gtfs-processor (see Next steps §4).
- national-czptt — the fetched CZPTT snapshot →
czptt-to-bundle. By default operational (non-passenger) points go only to the Parquet sidecars. - validate & publish — both production packages are validated and published atomically with
release.jsonand anartifact_root/current.jsonpointer.
--jobs and --memory-budget are forwarded to JDF, the overlay and CZPTT. No process or .NET
heap hard limit is configured; memory figures are telemetry.
- JDF: coordinates come from the
jrunify-ext-geodata/otherCSVs (checkedgapfill.csv, regional catalogues) and an OSM stop extract. Unmatched stops are route-estimated (estimated:route-time, orestimated:route-end-northpast the last anchor) and shown with[?]. Bundles carrycoordinate_precision,coordinate_sourceandcoordinates_missing. - CZPTT: SR70 coordinates are authoritative; OSM fills gaps. The build handles NAD (rail replacement), notes, accessibility, bicycles, request stops, same-month cancellation ordering, inventory convergence during live acquisition, and inconsistent-time correction (R10).
- Post estimator: opt-in (
--estimated-posts). Oběhy passes the learned scorer (policy v3,src/obehy/data/post-inference/learned-v1.json) unless--post-inference-policynames another; capture-only runs take no policy. JrUtil's own default is still the heuristicconservative-routed-v4|tuned-safe-v3|kostany-diagnostic-best-safe, which feature export uses as the training baseline. Evidence-v2 packs are captured only for retraining (--capture-post-inference-evidence); bundles always route live. - Serving: JrUtil production packages (
jrutil-productionbundle v3, serving schema 5.0): GTFS projected from the relations, bounded diagnostics and 19 typed Parquet relations (§6). - Mirror:
obehy db migrate,obehy release load <release-dir>,obehy release activate <run-id>|--rollback,obehy release status(src/obehy/release/). One LIST partition per package load, built and checked standalone and attached in one transaction; readers use theactive.*views. Derived:service_date, locationgeom,shape_line.obehy release fetchdownloads the newest GitHub build release intodata/releases/<run-id>, verified.
- Live
obehy build(estimated posts, learned-v1, routing cache), both published:20261002T184241Z: 45.3 min (JDF 19.4, overlay 4.7, CZPTT 20.1 min).20261003T144231Z(serving schema 4, seeded stop registry): 39.2 min (JDF 16.8, filtered JDF 0.5, overlay 5.0, CZPTT 16.0, validation 0.8 min). Zero stop, post or overlay-place registry candidates.20261006T194555Z(local, serving schema 5.0): all three packages valid; the dev database and the pinned corpus use it.- GitHub Actions builds daily since 2026-10-08; the server runs
20261009T030215Z-8cf199243726. - Not run on either: MobilityData GTFS validator, MOTIS import check.
- JrUtil Release suite: 309 tests (2026-10-06).
- Oběhy: 232 unit + 36 PostgreSQL tests, ruff, pyright and import contracts clean; the pinned DÚK corpus replays to its golden digests (2026-10-10).
- Mirror load of
20261006T194555Zinto PostGIS 17 over a ~10 MB/s LAN link: JDF 466 s (COPY 352 s, network-bound; trip_call 7.38M rows 270 s), CZPTT 81 s; activation instant. Departure boards at Česká Lípa, Duchcov and Nymburk hl.n. mix both feeds with platforms/posts. - Default JDF conversion: 179.4 s, 2.88 GB peak private memory, package valid (2026-09-15).
- National finalizer replay with overlays: 4.07 GiB peak, package valid (2026-09-19).
- Post-estimator frozen replay: bundle 13m04s vs ~43m33s live baseline, byte-identical payloads (2026-08-15).
- The live releases have not been through the GTFS validator or a MOTIS import check, so none is formally accepted yet.
- The stop-ID registry is seeded (37,598 stops, 57,237 posts, 119 overlay places), but ID
stability across consecutive builds has not been compared yet. Authored
:post:id:<n>posts keep the carrier's Označníky number; only inferred:est:<k>posts are pinned. - CZPTT
locationrows carry no municipality, district or country metadata (the CZPTT package has no stop metadata). - Serving validation checks keys, hashes and foreign keys; cross-representation content validation (GTFS vs serving relations) is incomplete.
- No route shapes are generated yet (MOTIS shape generation is future work).
- DPmÚL realtime coverage is poor: weekend trip numbers on weekdays and Friday nights (DUK-Q3,
DUK-Q16) stay unmatched by the literal-key rule, and some trip numbers are missing from the
release (DUK-Q17). Parked deliberately on 2026-10-10;
docs/sources/duk.mdhas the details.
Work order: §5 (core runtime) in BASE_PLAN.md section 34 order. R2 (DÚK trains + SŽ, rail
fusion, platforms, docs/R2_SLICE.md) is implemented; its acceptance waits for a CI release
with serving 5.1 and a 25 h corpus recorded against it. Platforms are off in GTFS-RT
(2026-10-10): boards number tracks for passengers, CZPTT by internal track numbers, so equal
designations were often different tracks. Board values are still recorded in
history.platform_evidence. After a few weeks: publish the board-numbered platforms as static
child stops unused by stop_times (as PID does), keyed by station and shown value, and match
board values to them in RT; never to CZPTT tracks. Then R3 (PID). §1, §4 and §6 are done; §2 and §3 wait on comparing consecutive Actions builds,
and static acceptance (MobilityData GTFS validator, MOTIS import) is still open.
- Goal: fewer route-estimated JDF stops, starting with runs of consecutive
[?]stops and inventedroute-end-northpositions. - Actions:
- refresh the geodata catalogues (
download.pyinto staging, diff, replace); - run
gapfill.py audit --coordinate-status estimated --minimum-run-length 3 --include-unresolved-terminion the latest bundle; - resolve unique OSM/Mapy/Nominatim identities into
other/gapfill.csv; - have the build write a
stop-coordinate-report.json.
- refresh the geodata catalogues (
- Accept: estimated and route-end-north counts drop against a recorded baseline; geodata tests pass.
- Status: done (catalogues refreshed and gapfill residuals resolved on 2026-10-02).
- Goal: stop IDs stay the same between exports.
- Design: the reviewed, append-only registry lives in
jrunify-ext-geodata/registry/(stops.csv,posts.csv,overlay_places.csv; see its README). JrUtil--stop-registry:merge-jdfpreloads registered identities, sojdf:stop:Ndoes not depend on batch order; same-named stops are split by reference coordinates and ambiguity is quarantined. Unregistered stops get a provisional number ≥ 1e9 hashed from name, okres and country.jdf-to-bundlekeeps inferredest:<k>ordinals within 25 m of a registered post.regional-gtfs-overlayuses pinned IDs for source-native stop places.--stop-registry-candidateslists what is missing;registry.py promoteadds reviewed rows.
- Oběhy: passes the registry when
registry/stops.csvexists, records its hashes in the national-JDF run manifest, and keeps the candidate CSVs inreleases/<run>/stop-registry/. - Accept: identical IDs across batch orders and consecutive builds; ambiguous stops are quarantined, not merged.
- Status: implemented, and seeded on 2026-10-03 (
jrunify-ext-geodata410457b): the planned stop-ID break is done. The 2026-10-03 release was built with it and produced zero candidates. Next: compare stop/post IDs of the next build against that release to confirm stability.
- Goal: a repeat build's post phase takes ≤15 min.
- Actions:
- profile one live run;
- add a persistent routing-graph cache keyed by PBF hash and format version;
- add a per-context evidence cache so a live build routes only new contexts;
- only if still dominant, deduplicate/one-to-many A*.
- Accept: payload hashes equal a cold live run on the same inputs.
- Status: 3 done (JrUtil
--routing-cache, on by default in Oběhy; entries revalidated against per-tile graph fingerprints). Routing also runs on up to 8 workers. 2 and 4 not started.
- Goal:
obehy buildpublishes JDF, CZPTT and a filtered JDF GTFS. - Filtered feed: drop lines of FlixBus CZ/DACH/Polska, PMDP and DPMO (operator or
Altdop) and of IDS PID/IDS JMK/IDZK (preferredLinExtrow) in the merged national JDF, every version valid on or after the build date. Also drop the checked-in line-number prefixes. Remove calls at stops without coordinates and keep[?]stops. Customs stops are handled in JrUtil, not in the filter. - CZPTT: operational points go to the sidecars only by default (
--czptt-operational-points gtfsrestores the old behavior). - Status: done. Implemented (
src/obehy/filtered_jdf.py, rules injrunify-ext-geodata/filtered-jdf/rules-v1.json) and published by both live builds (jdf-filtered/, about 30 s). MobilityData validation of the filtered feed is not run.
- Design:
BASE_PLAN.mdsections 1, 16 and 18–23 (release mirror, fact-based trip inference, interval timeline engine with progress integrity, own GPS delay, circulations). Feeds are built on GitHub Actions; the server only fetches, loads and serves. Realtime order: DÚK + SŽ, then PID (Golemio APIs + GTFS-RT alerts), then Arriva Express. - Steps:
obehy rt recordand dossiers for DÚK, SŽ and Arriva Express (docs/sources/), checked by replaying a 25-hour capture against the v5 release: DÚK buses 99.1% of running trips to exactly one trip (DPmÚL pending the JrUtil merge fix), DÚK trains 95.3%, SŽ 99.7% of train-days to one run, Arriva Express 61/61 scoreable trips by line, destination and next-stop times.- Serving schema 5 (§6; replaces the planned CZPTT contract check).
- Database foundation and
obehy release fetch|load|activate --rollback. - Realtime skeleton: model, clock, archive, core loop,
rtmigrations, replay. - Inference engine (facts, scorers, decision rule, date inference, vehicle binding, rail runs).
- Timeline engine (intervals, delay semantics, progress integrity, arbitration).
- DÚK connector and per-feed GTFS-RT; 8. SŽ and rail fusion; 9. project API; 10. PID; 11. Arriva Express with own GPS delay; 12. circulation learning.
- Accept: per step as listed in
BASE_PLAN.mdsections 33–34; scenario tables for inference and the timeline engine; deterministic replay; GTFS-RT validator on replayed days. - Status: step 1 done (2026-10-06; recording continues). Step 2 done (§6). Step 3 done
(
release fetch2026-10-09). The server runsdeploy/compose.yamlfrom the CI-built image since 2026-10-09: hourly fetch → load →activate --if-newer, the realtime worker, nightly jobs at 03:30, and nginx serving/get-feeds/(static feeds, GTFS-RT, feed list) behind the host's reverse proxy. The MOTIS node takes its feeds from there. Step 4:obehy rt replay(archive → episodes → trips of a Parquet release, deterministic report) done 2026-10-08. The core architecture is decided (2026-10-08,BASE_PLAN.mdsections 5, 18–22, 29–30). The first slice (DÚK buses with history, warm-replay restart,docs/R1_SLICE.md) is implemented (2026-10-09): times, model, lazy index, keyed binding, progress as map matching, history, DÚK connector, worker, warm replay and replay on the core. On the pinned 25 h corpus the match rate is DÚK 99.4%, Teplice 99.1%, DPmÚL 54.7% and the GTFS-RT consistency check is clean. Replay is deterministic (byte-identical snapshots across runs; golden digests intests/golden,tests/db/test_corpus.pywithOBEHY_PINNED_ROOT). Open for R1 acceptance (§6 there): history rebuilt twice and the warm-restart comparison on the real corpus (both pass as DB tests on fixtures; the corpus run was stopped, slow over the LAN), and the MobilityData GTFS-RT validator (needs aread:packagestoken). R2 (DÚK trains + SŽ,docs/R2_SLICE.md) is implemented (2026-10-10): proxied anonymous SŽ egress, serving 5.1sr70/sr70:trackkeys (JrUtil), rail runs with train-number journeys andjourney_link, the SŽ map connector, DÚK + SŽ fusion, station boards with platforms. A 2 h replay (2026-10-06 07-09, keys derived for the measurement) binds 1,653/1,660 SŽ and 142/150 DÚK train key groups, places 99.5% of SŽ point events, and its rail GTFS-RT passes the consistency check. Open for R2 acceptance: a CI release with serving 5.1, then a 25 h DÚK + SŽ + boards corpus pinned against it (golden digests, validator, live hour). Predictions followBASE_PLAN.md20.5 (dwell recovery, early running carried over, no uncertainty); per-stop holding and knock-on to the next trip of a tour wait for history learning and circulations (section 22). JrUtil'sserving-v5.jsontime description still says noon − 12 h and needs correcting. - Pin 2026-10-25 (DST fall-back) from the running recorder as the first permanent replay corpus.
- Goal: a minimal, easy-to-query serving contract before the loader is written. v4 has 30
relations, keeps rail operational points apart from trips, and spreads realtime keys over seven
binding relations (JDF
source_call_map: 16.57M rows, all but 10,209 identity). - Design: JrUtil
contracts/serving-v5.json(5.0 draft) anddocs/PRODUCTION_CONTRACT.md: 19 relations; onetrip_callsequence including CZPTT railway points (NAD bus parts carry none);source_key/call_keylookups with per-namespace identifier encodings; closed enumerations, including the typed JDF feature kinds; feed-prefixed IDs; GTFS-style times; zones on route stops with call exceptions; calendars as in v4; lossless typed semantics inservice_note,assignment(with CZPTT call ranges),connection_claim,travel_restriction; boarding-point platform/post codes; GTFS projected from the relations. Versions aremajor.minor; minors only add. - Validation: a v5 prototype (DuckDB views over the 2026-10-03 v4 packages) ran the
consumer scenarios. DÚK (CIS line + trip), SŽ (TR id, train number, run timeline at Nový Bor),
Arriva Express (line + destination + time) each resolve to exactly the trip their dossier
names. A mixed rail/bus departure board at Česká Lípa hl.n. and a vehicle-detail query
(schedule, zones, notes, features, restrictions, claims) run as plain joins. The NeTEx mapping
in
JDF_SEMANTICS.mdfinds a home for every fact. Contract fixes found on the way: typed kinds, platform codes, CZPTT note ranges, source-qualified namespaces, feed-prefixed IDs, and no reserved words (key,method). - Status: done (2026-10-06). JrUtil writes and validates 5.0 natively (bundle 3): typed JDF
kinds, CZPTT trip parts with railway points and
call_rangenotes,source_key/call_key, GTFS projected from the relations, CZPTT input digest in the manifest. Oběhy accepts serving major 5. A full local build produced three valid packages and the realtime replay (§5.1) resolves against them; the v4-vs-v5 comparison was dropped (different source data). Supersedes §5.2: the CZPTT trip ↔ operational-call link istrip_callitself.
-
2026-10-10 — DUK-Q1 corrected: Teplice buses and RJÚK trains apply the local offset twice to their GPS time (
LastActivityDTis right); the old "UTC digits" reading put them 4 h ahead, so all their positions were dropped. Now detected per entry against the unit's activity time, only ever subtracted. Corpus: every Teplice/206xxentry fixed, no other vehicle touched; Teplice positions 0 → 5,357 in the bus golden snapshots (digests regenerated). Open: a Teplice bus switching to its next trip while finishing the last over the same streets reads as 19 min early (585101:65). -
2026-10-10 — Early running only when observed. 28 DÚK corpus journeys were "30+ min early": the tracker took a loop route's second pass at the start (Klášterec
527356), and DPmÚL vehicles say running on a stand past the first stop under the next trip (DUK-Q19). Fixed with an asymmetric first-fix prior (early sigma 2 min); before the start, a running state counts only at the first stop or when the vehicle is seen moving along the path, plausibly early. A vehicle standing at a stop ahead of time is waiting, not running early (7% of bus stop predictions later, median +42 s). Trains never get a predicted early departure (max_early_departure_s, rail 0). Now 0 journeys under −30 min; the rest under −10 min are GPS-observed (line001590runs ~10 min ahead of its 2024-12 timetable all day). Golden digests regenerated. -
2026-10-10 — R2 implemented (
docs/R2_SLICE.md):- SŽ requests only through a proxy pool from secret config, with no User-Agent, failing closed; a 403 opens the circuit.
- JrUtil serving 5.1 publishes
sr70/sr70:tracklocation keys. - Rail runs: one instance per CZPTT path, journeys per train number with
journey_link, GTFS-RT per part (0007_rail.sql). - SŽ map connector (Křovák via pyproj, NÁZEV20 names) and DÚK + SŽ fusion with the
anchor_changepredictor, which beat SŽ's own prediction and plain propagation in replay. - Station boards read on demand (about 64/min nationally at noon, 60/min ceiling) give platforms; only tracks reach GTFS-RT.
- Fixed replay tagging every poll with the last channel when replaying several.
- A train is one GTFS-RT vehicle (opaque per-run ID, train-number label), not one per coupled DÚK unit plus SŽ.
- Validated: 283 unit + 39 DB tests, JrUtil 313 tests, the DÚK golden corpus unchanged, a 2 h rail replay clean. Open: the R2 corpus and acceptance.
-
2026-10-10 —
docs/R2_SLICE.md(rail slice contract) anddocs/sources/sz-tabule.md(SŽ station boards, probed by hand: one request per station, about 20 arrivals and 20 departures, platforms about 60 min ahead, numbered by platform at big stations and by track at small ones; big-station platforms are shown labels only and stay out of GTFS-RT). SŽ is reported to IP-ban services: all SŽ requests will go through a proxy pool without a User-Agent. No code yet. -
2026-10-10 —
docs/sources/dpmd.md: dossier for the DPMD (Děčín city buses) vehicle map, from its page script and a 3-minute capture. Keys are515<line>:<trip>(all 15 DPMD lines in the release), 8 of 9 live trips resolve; quirks DPMD-Q1–Q9. No connector or 25-hour capture yet. -
2026-10-10 — Source-specific readings in the core come from the manifest's
[channel.semantics](SourceSemantics): DÚK setspre_trip_delay_is_elapsed(DUK-Q14) andpre_trip_after_end_is_stale(DUK-Q15); other sources take their facts at face value. The DUK-Q18 missed-departure rule applies to every source. -
2026-10-10 — DUK-Q18: a bus whose onboard unit missed the departure (State 3 all trip long,
TODepartureDTrolled to the next run) runs once it is on the path past the first stop after the scheduled start, so it gets GPS delays instead of a bare position. NewSourceDeparturefact; DÚK decoder version 2. -
2026-10-10 — The feed list page and
feeds.jsonname the realtime sources and what they cover (DÚK vehicle positions →cz-jdf-gtfs-rt.pb). -
2026-10-10 — The server stack serves feeds with stock nginx instead of Caddy, for its per-download
limit_rate(about 100 Mbit/s); the legacy/gtfs-rt/jdf.pbroute is gone, the realtime feed is/get-feeds/cz-jdf-gtfs-rt.pb. Config not run here (no Docker). -
2026-10-10 —
/get-feeds/on the server stack: the active release's JDF, filtered JDF and CZPTT GTFS plus the live GTFS-RT under stable names (those of the MOTIS node's old directory), with a list page andfeeds.jsonwritten byobehy release feed-listafter each release update. Unit-tested; the Caddy routes are not run here (no Docker). -
2026-10-10 — DPmÚL gaps from the first live night documented and parked: Friday-night weekend numbers on night lines (DUK-Q16), trip numbers missing from the release (DUK-Q17).
-
2026-10-10 — First live day exposed DUK-Q14/Q15: a bus in the depot in State 3 with its evening trip's key got that trip predicted 113 min late from DÚK's pre-departure
Delay. Source delay is now ignored before departure, and a pre-departure key after the trip's scheduled end isstale_key(unbound, unstarted journey dropped). Pinned corpus: 15,666 observations newlystale_key, running match rates unchanged, GTFS-RT check clean, golden digests regenerated. -
2026-10-09 — Server image (
Dockerfile, built and pushed to GHCR by CI on green main, with a smoke test) anddeploy/compose.yaml: PostGIS, migrate, hourly release update, realtime worker, nightly jobs at 03:30, Caddy serving the GTFS-RT and the activegtfs.zip. Replaces the systemd units. Not built locally (no Docker here); the first CI run is the check. -
2026-10-09 —
obehy jobs nightly(vehicle days of the last two service dates, then droprt.observationdays older than 30, policy[retention]) run at 03:30 Prague by the server stack. DB test for the drop; raw-archive retention is still missing. -
2026-10-09 —
release activate --if-newer(newer than every release published before, so a rollback sticks) anddeploy/systemd service + hourly timer running fetch → load → activate. DB test added; the units are not yet installed on a server. -
2026-10-09 —
obehy release fetch: newest (or named)build-*GitHub release, assets checked against GitHub's digests while streaming, packages againstrelease.jsonand their manifests, unpacked atomically, three newest kept. Unit-tested with a fake GitHub; the real20261009T030215Z-8cf199243726(800 MB) fetched and verified in 43 s, a re-run skips it. -
2026-10-09 — Realtime through reception gaps: predictions use the tracker's GPS lateness (source delay as fallback) and hold it through gaps; a silent journey loses its position after
stale_after_sand its predictions afterpredict_without_data_s(per mode, longer for rail), counted on the reception clock; calls passed in a gap get an ex-postinferredtime but no history event. Tracking: lateness against the dwell window (waiting at a stop is not early), off-path option instead of killing a reading, chord sigma capped, speed slack is GPS jitter only, commit agreement among non-negligible readings, crossing times placed by timetable time. 522586:107 checked tick by tick (frozen GPS clock at the origin, loop off the chord, gap before Vejprty). -
2026-10-09 — Progress rebuilt as map matching (
timeline/progress.py): beam of hypotheses, commit only events all hypotheses agree on (same bracketing fixes), events overmax_event_interval_sdropped, DUK-Q13 (Azimut 0 = no bearing). 12 scenario tests + real závlek traces (001521:104, 522586:103/107/111, 522591:112) pass. Not yet re-run: full replay GTFS-RT check on this commit, history replay, golden digests (tests/goldenuncommitted). -
2026-10-09 — Progress redesigned (docs): greedy per-fix projection could not handle závleky, loops, stops passed before being served, corner-cutting chords and reception gaps (001521:104 skipped a branch; 522586:103 jumped across one after a gap). Replaced in
BASE_PLAN.md20.4 /docs/R1_SLICE.md9 by map matching over the trip's path with a beam of hypotheses and commit-when-all-agree. Implementation next. -
2026-10-09 — Realtime speed: 290 → 85 µs per observation (25 h DÚK replay 7m17s → 2m20s): projection only within reachable distance of current progress (policy
max_speed_mps; full scan after gaps), shapes simplified to ~5 m, out-of-tolerance segments skipped, estimates only at emit time, scheduled instants cached, replay prefetches keys 40 polls ahead. Bindings unchanged (identical reasons and rates); 3 % of trip updates changed, because a fix can no longer snap several stops ahead at once. Real replay of 2026-10-05/06 on release 20261006T194555Z: running key groups bound DÚK 99.4 %, Teplice 99.1 %, DPmÚL 54.7 % (old resolver 99.1 / 96.7 / 52.7 % on the same release). -
2026-10-09 — R1 pipeline complete in code: per-connector manifests (
realtime/sources/*.toml, recorder unchanged in behaviour), DÚK connector with quirk tests, shared scheduler,Runner(lazy index, invariants),obehy rt replayrebuilt on it (GTFS-RT snapshots,--write-history),obehy realtimeworker (warm replay, emit tick, release rebase on NOTIFY),obehy jobs vehicle-day,obehy rt corpus pin. Legacyresolve/episodes/decode/release_indexremoved. End-to-end DB tests: deterministic replay and history rebuild, warm restart equals an uninterrupted run, mid-day release switch orphans vanished calls. Not yet run on the real corpus. -
2026-10-09 — Migrations 0005
rtand 0006history(journey-keyed, partitioned by service-date month,tour_idreserved);emit/db.Writer(COPY observations, revisioned snapshots with orphaning, events, assignments, current state,clear_history, partitions on demand); per-feed GTFS-RT (matched only, deterministic);jobs.vehicle_daySQL (T13). DB tests pass on the remote dev database. -
2026-10-09 — Timeline (single source): path from shape or straight stops, monotone progress with backtrack tolerance, off-route hold/flag/rejoin, arrival and departure events as fix-bounded intervals (loops attach to the right visit), predictions from the source delay with monotone repair,
pre_trip→running→finished. 10 scenario tests incl. DUK-Q5/Q6.staleis set at emit time (ticket 6). -
2026-10-09 —
core.stepwith keyed binding (infer/keyed.py: date rule of §19.3, binding continuity, reason codes), vehicle states and the pre-trip lifecycle; scenario tests T2–T5, DUK-Q3, DUK-Q4, DUK-Q11 pass. The timeline is still a stub that only tracks delay. -
2026-10-09 — Realtime foundation:
model.py(facts, journeys, state, effects, JSON round-trip),policy-v1.toml+ loader,index.py, the test timetable builder and the import-linter purity contract;index_sql.IndexLoaderfills the index lazily by source key fromstatic.*by load_id, andensure_releaseloads a release directory on demand. DB tests (index equals builder) pass against the remote dev database. -
2026-10-09 —
realtime/times.py(T1, T6–T12, T14, T15 pass) and its SQL twincontrol.obehy_instant(migration 0004; agrees with Python on the DST cases against the remote dev database). ruffDTZon, with ignores only for the legacydecode.pyand its test. DB tests read[database] test_urlfrom the local config. -
2026-10-09 —
docs/R1_SLICE.md: core types, time scenario table T1–T14,rt/historyDDL sketch, connector manifest and policy shapes, fixtures, R1 acceptance. Restart by warm replay replaces the checkpoint; stabletour_idadded to section 22. Docs only. Schedule times are wall-clock (DST nights may be slightly wrong); nonexistent or skewed source times are dropped, never guessed. -
2026-10-08 —
ARCHITECTURE.md(Mermaid diagrams) and the remaining long-lived decisions (docs only).- Connector contract: polled channels and lazy lookups (
plan→fetch→decode), run by a generic scheduler and lookup broker; the API never calls upstream. - Capability routing, including minimal "line + trip + delay" sources.
- Vehicles: identity levels, minted
vehicle_id, curated files viaobehy ref import, the effective-attribute view, media. - Compositions as journey segments, separate from
vehicle_assignment. - Four circulation sources with their precedence (§22, §27–28).
- Fixed now:
- rail journey = train number + date with
journey_link; - core partitioned by feed;
- observation envelope and lookup replay;
- journey-key public URLs and a minimal public realtime model;
- no human input.
- rail journey = train number + date with
- Validated: diagrams previewed in light and dark mode,
git diff --check; no code changed.
- Connector contract: polled channels and lazy lookups (
-
2026-10-08 — Core runtime architecture decided (docs only).
BASE_PLAN.md:- module layout with an import-linter layering rule;
core.stepshared by worker and replay; SQL for set-wise work; - release index built from
active.*in PostgreSQL,release_idon every realtime row; - literal keyed matching, with keyless inference isolated;
- time rules in one
times.py(§19.3); - multi-source arbitration with the "anchor plus predicted change" rule (§20.6);
- trip lifecycle and off-route grace (§20.7);
- circulation edges keyed by literal keys, with a fingerprint validity guard (§22);
- journey-keyed, self-describing, revisioned history (§29);
- API v1, PMTiles and vehicle states (§30);
- rolling raw window plus pinned corpora (§18.4).
- module layout with an import-linter layering rule;
AGENTS.mdgains realtime code rules and documentation roles. The dossiers gain quirk ledgers (DUK-Q1–Q12, SZ-Q1–Q9, ARRIVA-Q1–Q7).- Stale v4 references fixed in README, STATIC_PIPELINE and JDF_SEMANTICS (v5 counts from
20261006T194555Z). - Validated:
git diff --checkand review only; no code changed.
-
2026-10-08 — DPmÚL replay against the 2026-10-08 packages (JrUtil
17179c9): 1,573 of 1,973 running episodes resolve (79.7%; 52.7% before the merge fix).- Most of the rest is upstream, not a JrUtil bug: 268 of 275 off-calendar episodes are
weekend (
3xx) trip numbers reported briefly on weekdays, mostly between two resolved trips. Line 595200's JDF (batches 4312, 10217) and the release calendar agree. - Inference must not rebind a vehicle on such keys. Details are in
duk.md. - The 7 DÚK-range losses are lines whose 2026-10-06 version is no longer in the release.
- Most of the rest is upstream, not a JrUtil bug: 268 of 275 off-calendar episodes are
weekend (
-
2026-10-08 —
obehy rt replay(§5 step 4, replay only).src/obehy/realtime/:decode(DÚK/SŽ/Arriva payloads → rows: fleets, Teplice UTC and Arriva local-time fixes, SŽ TR keys),release_index(pyarrow indexes over the serving Parquet: keys, services, trip/run spans, calls),episodes,resolve(the dossier rules, SŽ train-number fallback, Arriva next-stop scoring, stale-repeat collapse),replay(report.json+episodes.parquet). No DB, no new dependencies.- Validated: 6 new unit tests (134 total), ruff, pyright. The 25-hour capture against
20261006T194555Zreproduces every dossier table and is byte-identical across runs: 41 s, 2.5 GiB peak (all decoded rows are held in memory, so it grows with the range). - Correction: the 19 unresolved DÚK train episodes have CZPTT train-number keys, but no
timetable for them runs that day (
duk.mdsaid they were missing from the package).
-
2026-10-08 — CZPTT source cache and download concurrency.
- GH Actions spent 1h+ fetching ~170k sub-KB objects over 8 connections (round-trip bound).
Objects are now cached across runs (
cache/czptt-sources, rolling Actions cache) and fetched over 32 connections independent of--jobs. Unit tests, ruff, pyright pass; no live-portal or GH run yet.
- GH Actions spent 1h+ fetching ~170k sub-KB objects over 8 connections (round-trip bound).
Objects are now cached across runs (
-
2026-10-08 — Reviewed Czech data moved to jrunify-ext-geodata; route presentation overrides.
routes/transport-modes.csv(wasobehy/data/jdf_transport_mode_rules.csv),overlay/pid-stop-overrides.csvandfiltered-jdf/rules-v1.jsonnow live in the geodata checkout; the run manifest records them underroute_rules. The empty CZPTT OSM alias file and JrUtil's--osm-aliasesare gone: foreign passenger CZPTT points without coordinates fail.- New
routes/presentation.csvoverrides route marking and colours by licence (exact, range, prefix) and optional agency IČO; JrUtil applies it injdf-to-bundleand again afterregional-gtfs-overlay, so it wins over PID/IDS JMK values. Route IDs are unaffected. - Validated: JrUtil tests (313), obehy unit tests, ruff, pyright, geodata
test_routes. No real-data run yet; the rules file is still empty.
-
2026-10-08 — Sources fetched up front; filtered JDF from LinExt instead of the line portal.
- Three CI builds failed ~45 min in on a TLS handshake timeout to portal.radekpapez.cz (no retry,
no source named). The portal's IDS query equals preferred
LinExtrows (PID 1921/1921, IDS JMK 1208/1208 on 2026-10-08); on that day's merged JDF the derived filter matches the portal except 235005 (no LinExt row in any source version, accepted) and adds DPMO 895xxx, PMDP 446007 and six FlixBus-co-operated international lines (Altdop).--line-filter-snapshotis gone. - New
fetch-sourcesstage before the JrUtil build (build_sources.py): national JDF and CZPTT become source snapshots for their builders; every download retries 3× (5 s, 15 s) and fails asDownload <source> failed after N attempts: <url>: <reason>;release.jsongainsretrieval. Validated: unit tests, ruff, pyright; live fetch of JDF/PID/IDS JMK (17.5 s) and a refused host (named error after 26 s). CZPTT fetching only via unit tests; no CI run yet. - Not done: LinExt is not in the serving contract; its secondary rows (another system's tariff
valid on a line) would need a
route_integrationrelation for fares.
- Three CI builds failed ~45 min in on a TLS handshake timeout to portal.radekpapez.cz (no retry,
no source named). The portal's IDS query equals preferred
-
2026-10-07 — Release mirror (§5.3, milestone C1 without fetch).
src/obehy/release/: migration runner (control.schema_migration, checksums),controlschema (release, package, load, publication + history), static DDL generated from the vendoredserving-v5.json(19 relations +service_date,shape_line, locationgeom), loader (hash/contract verification, COPY via pyarrow CSV, PK/index build, set-wise FK and row-count checks, unknown-enum warnings, attach in one transaction), activation through regeneratedactive.*views with NOTIFY, stack rollback, retention (active + 2 predecessors + staged loads). New deps: psycopg 3, pyarrow.compose.yamlfor PostGIS 17; CI runs the DB tests in a postgis service.- Validation: 129 tests (13 against PostgreSQL), ruff, pyright. Real load and activation of
20261006T194555Z; the serving-v4 release20261003T144231Zis rejected. - Finding for the API: departure boards must drop each trip's final passenger call. PID leaves pickup allowed there, and CZPTT run parts meet at the line change (Os 8503 arrives at Nymburk as S31 and continues as S2 → Poděbrady, linked by an in-seat transfer).
- Remaining:
release fetch; GTFS paths and resolver version in the publication; inference indexes.
-
2026-10-06 — Realtime replay (§5.1): 25 hours of DÚK, SŽ and Arriva Express payloads resolved against the v5 release; dossiers updated with the results. Findings: Arriva's
lastStopNameis the next stop andupdatedis local time; DÚKDelayis signed (unsigned for the DPmÚL and Teplice fleets), DÚK also carries trains (resolved through CZPTT by train number), and vehicles keep stale trip keys while positioning; DÚK vehicle chains are 90% same-stop links. Fixes in JrUtil found along the way: overlaycall_keypositions (0d85ece), CZPTT call features and stopless in-seat transfers (b65cc9b), and date-by-date JDF version resolution for DPmÚL's parallel versions (6a86278; bounded old/new merge comparison on 804 urban-rail batches and 518 bus lines: no overlapping versions, only corrections). Validation: JrUtildotnet test309/309. The replay became a repo tool on 2026-10-08. -
2026-10-06 — Serving schema 5.0 implemented in JrUtil (§6): writer,
validate-packageenumeration/prefix/key-encoding/trip-part/GTFS-projection/source-digest checks, contractserving-v5.jsonnormative,serving-v4.jsonremoved, golden expectationserving-v5.txt. Oběhy's manifest gate accepts any5.<minor>. Validation: JrUtildotnet test304/304; Oběhy unit tests, ruff, pyright clean. Skipped: the bounded real-data run (no golden inputs on disk). -
2026-10-05 — Serving schema 5 drafted (§6) after a v4 fitness check on the 2026-10-03 release: CZPTT trips already link to their PA through
czptt_pa_idbindings (all 583,171 calls align, 53 corrected times differ); GTFSstop_timesequal the passengertrip_calls in both feeds; CZPTTsource_snapshot_sha256is all zeros;location.domainisscheduledeverywhere. The planned CZPTT contract checks and the DB foundation were dropped in favour of v5.BASE_PLAN.mdnow targets v5. Validation: documentation and draft JSON only. -
2026-10-05 — daily
buildworkflow (.github/workflows/build.yml).- 02:30 UTC and on demand:
obehy build --estimated-posts --memory-budget 8GiBonubuntu-latest, work on/mnt, JrUtil and geodata from their standalonemain. - Caches the merged OSM snapshot plus derived extracts per month (
--refresh-osmonly on a miss) and the routing cache per run; publishesbuild-<run-id>Releases (release.json + one tar per package), keeps 14; run logs/diagnostics as 14-day artifacts. - Validation: actionlint clean. Not yet run on Actions.
- 02:30 UTC and on demand:
-
2026-10-05 —
obehy rt record(core runtime step 1).- Polls the channels in
src/obehy/data/realtime/sources.toml(DÚK 15 s, SŽ 30 s, Arriva 30 s) intodata/rt-raw/<source>/<channel>/<UTC date>/: anindex.jsonlline per poll (errors included) and content-addressed zstd objects. New dependency:zstandard. - Arriva's fleet-wide GraphQL feed is reduced to Arriva Express before storage
(
arriva-express@1; source size and hash are kept in the index). Arriva exposes no trip number: introspection is off, no trip fields or arguments exist, and there is no detail endpoint. - Validation: Oběhy unit 91, ruff and pyright clean; a live
--oncerun and a 2-minute run of all three channels. Storage is about 6.5 KB per DÚK poll and 28 KB per SŽ poll (about 120 MB/day). Remaining: record several days on Linux, then answer the dossiers' open questions from the capture.
- Polls the channels in
-
2026-10-05 — JrUtil
d26bf0b:location.district_codenow carries the JDF okres code, and the regional overlay keeps base location metadata (municipality, district, nearby place, country, coordinate precision); before, every one of these columns was null in the publishedjdfpackage. New overlay test; Release suite 303 passed. Takes effect with the next build. -
2026-10-05 — Architecture rewrite of
BASE_PLAN.mdfor the core runtime.- Decided: two feeds (
jdf,czptt); the file registry is the permanent identity model (no identity service;IDENTITY_REGISTRY.mdremoved;jrutil-identity-v1is final); builds on GitHub Actions; one realtime process with in-memory state and a Postgres log. - New realtime design: fact-based trip inference with date inference, rail runs, interval timeline engine, progress integrity on A→B→A routes, own GPS delay, pluggable travel-time providers for long segments only, learned circulations.
- Validation: documentation only; no code or tests changed.
- Decided: two feeds (
-
2026-10-02/03 — Two complete live
obehy buildruns published (20261002T184241Z,20261003T144231Z; timings under Last validation evidence). The 2026-10-03 run used the freshly seeded stop registry and serving schema 4. An earlier 2026-10-03 attempt failed on a missing JDF post-candidate OSM extract (obehy-osm buildfixes it). -
2026-10-03 — Serving schema 4 (bundle v3), the final pre-core contract revision.
- Zones:
fare_system,fare_zoneremoved; zones are codes with an optional system, kept on the route stop slot when all its calls agree, otherwise on the calls;location_zoneis the per-stop union. route_stop: merged per route direction from the final calls of every producer (LCS alignment, one slot per visit);trip_call.route_stop_idand restrictions point at it.- Removed
route_segment,identifier_alias,object_origin,binding_evidenceand the nullsource_run_id/source_duty_id;validate-packagechecks foreign keys. - Oběhy accepts only bundle v3 / schema v4.
- Validation: JrUtil Release 302 tests; Oběhy unit 75, ruff and pyright clean. No golden-subset or live run yet: route-stop merge cost, zone row counts and FK validation time on national data are unmeasured.
- Zones:
-
2026-10-03 — Stop-ID registry (§2).
- JrUtil
--stop-registry/--stop-registry-candidatesformerge-jdf,jdf-to-bundleandregional-gtfs-overlay;source_stop_metadatagainsokresandstop_id_provisional. - jrunify-ext-geodata
registry/plusregistry.py validate|promote. - Oběhy passes the registry and keeps the review CSVs in the release.
- Validation: JrUtil Release 296 tests; Oběhy unit 75, ruff and pyright clean; geodata
test_registrypasses. No live or bounded real-data run yet.
- JrUtil
-
2026-10-02 — Speed pass and routing cache.
- JrUtil: CZPTT 305 → 33 s, bundle-posts 392 → 182 s, overlay 232 → 170 s on the golden subset; CZPTT, overlay and the no-posts bundle are byte-identical.
- Road thread identities are now deterministic (canonical walk); a few estimated posts change.
obehy buildpasses--routing-cacheby default (workdir/cache/routing,--no-routing-cacheto disable). Entries are revalidated per graph tile, so they survive daily demand clips and monthly OSM updates where the network did not change.
-
2026-10-01 — Learned post scorer by default; post-inference and overlay cuts.
obehy build --estimated-postspasseslearned-v1.jsonunless another policy is named. On the golden subset, calls left at the stop centroid fall from 354k to 186k.- Removed evidence-backed replay, review GeoJSON, diagnostic labels, the
derived_post_scorestable, side groups and same-stop pairs. Bundles always route live; capture andjdf-export-post-featuresremain for retraining. - Overlay: one combined-source path (profile schema v4, capabilities as a list); calibration, publication flags, coverage floors, route/trip overrides and unread reports removed. The PID Národní třída stop override now applies (it was silently ignored).
- Validation: JrUtil Release 274, post-scorer 26, Oběhy unit 70, ruff and pyright clean.
Golden gate
learned→cuts: GTFS, serving and diagnostics identical in all four packages; only compiler provenance and build-spec hashes differ.