Ambiguities and possible errata in TODS v2.1.0 (adopted 2025-04-16) found while implementing this validator. Where the spec is ambiguous, the validator takes the permissive interpretation noted under each entry, and the relevant rule ID is given so the behavior is traceable. These are offered as candidates for issues or discussion on the specification repository, not as assertions that the spec is wrong; happy to open PRs for the clear-cut ones.
Each entry cites the spec section it refers to. Verified against the spec
reference (docs/en/spec/index.md) and examples.md on
MobilityData/transit-operational-data-standard main, which matches the
published spec at https://tods-transit.org/.
The spec names the ten files and their filename conventions (the _supplement
suffix and the exact filenames), but never says how a TODS dataset is
physically packaged or distributed: a zip, a directory, or how a consumer
discovers and retrieves the files. It frames TODS as a typically non-public
layer separate from the public GTFS feed, but the packaging and transport
format is never formalized.
Reference: the Files table (spec) defines
the filenames and the _supplement suffix; no section specifies a packaging,
bundling, or discovery format.
Validator behavior: accepts a directory or a .zip of top-level .txt files,
with or without GTFS files in the same package. If GTFS files are present they
are used as the companion feed.
In the spec's
Run as Directed work example,
the last two rows of run_events.txt both use event_sequence 30 on run
(weekday, 10000):
weekday,10000,30,BLOCK-A,run-as-directed,...
weekday,10000,30,BLOCK-A,deadhead,...run_events.txt declares Primary Key (service_id, run_id, event_sequence),
and the event_sequence field note says it "Must be unique within one
(service_id, run_id)", so this example data is invalid. Presumably the
second row should be sequence 40.
Reference: run_events.txt Primary Key and event_sequence field note
(spec); the example
(examples).
Validator behavior: reported as TODS-E204 (duplicate primary key).
Several example CSV snippets pad values with spaces for column alignment,
e.g. weekday,10000,10, ,sign-in ,.... GTFS-style CSV has no
trimming rule for values, so a literal reading produces block_id / event_type
values that include the spaces. Are consumers expected to trim?
Reference: the run_events.txt blocks in the
Run as Directed work
and "Jobs of entirely nonrevenue operations" examples.
Validator behavior: values are compared exactly; padding is flagged as a
warning (TODS-W206) because padded IDs will not match their referents.
The run_events.txt field descriptions refer to mid_trip_start and
mid_trip_end in prose ("If trip_id is set (and mid_trip_start is not
1)...") while the field table defines the fields as start_mid_trip and
end_mid_trip. Presumably the same fields are meant.
Reference: the start_location / start_time field descriptions versus the
start_mid_trip / end_mid_trip rows in the run_events.txt field table
(spec).
start_time and end_time are typed Time in the run_events.txt field
table, but the spec never defines Time: there is no field-types or
term-definitions section, and the format is never stated. GTFS time allows
hours >= 24:00:00 for service past midnight, but no example demonstrates that
case (the after-midnight "Jobs of entirely nonrevenue operations" example uses
00:45 to 03:00, and every example time stays under 24:00:00), so whether GTFS
time syntax is intended for these fields is left open. The spec also does not
state whether an event's end_time must be greater than or equal to its
start_time (zero-duration events are explicitly allowed; negative durations
are never mentioned).
Reference: the Time-typed start_time / end_time rows in the
run_events.txt field table (spec);
the spec's only GTFS term-definitions link is for service day on
vehicle_assignments.service_id, not for Time. Field naming (not format) was
settled in
issue #48.
Validator behavior: GTFS time syntax including hours >= 24:00:00 is accepted
(malformed times are TODS-E203); end_time earlier than start_time is an
error (TODS-E401).
employee_run_dates.txt declares Primary Key: *, but TODS never defines what
* means: it has no conventions or terms section, and it states GTFS field
inheritance only for Supplement files, not for its TODS-Specific files. Under
GTFS's * convention (all fields jointly form the key) an exactly-duplicated
row would be invalid, but because TODS does not state this for its own files,
the status of a fully identical duplicate row (same employee, run, and date
twice) is left implicit. The prose only discusses run-and-date combinations
recurring across different employees, which have distinct keys.
Reference: employee_run_dates.txt Primary Key
(spec); TODS states
GTFS-convention inheritance only for Supplement files and has no section
defining *.
Validator behavior: exact duplicates are flagged as a warning (TODS-W408);
under a strict reading of * they are arguably invalid, so this is a deliberate
leniency.
A supplement row that adds a new entity (e.g. a new trip) does not have to
carry all fields GTFS marks required, and the spec only says the merged result
"should form a valid GTFS dataset" with limited exceptions. It is unclear how
strictly consumers should hold added rows to GTFS requirements, e.g. a trip
added without a service_id.
Reference: the dataset-modification rules: "the resulting data ('TODS-Supplemented GTFS') should form a valid GTFS dataset, with the limited exception of missing data that should be ignored" (spec). Validator behavior: only the primary-key fields are required on supplement rows; full GTFS validation is delegated to gtfs-validator run on the merged feed.
service_id is "Required if block_ids are repeated between different
service_ids." Read literally, one repeated block anywhere in the feed makes
the field required on every row. The more useful reading is per-row: required
where that row's block_id is ambiguous.
Reference: the vehicle_assignments.service_id field condition
(spec).
Validator behavior: per-row interpretation (TODS-E205 fires only for rows
whose block is used by more than one service).