Skip to content

feat: add station first-last train schema#298

Open
duncanleo wants to merge 4 commits into
mainfrom
codex/station-first-last-layout-data
Open

feat: add station first-last train schema#298
duncanleo wants to merge 4 commits into
mainfrom
codex/station-first-last-layout-data

Conversation

@duncanleo
Copy link
Copy Markdown
Member

@duncanleo duncanleo commented Jun 1, 2026

Summary

  • Add grouped station firstLastTrain schemas with normal and recurring special calendar timing maps.
  • Add station layout schema support and repository validation for layout references, platform service coverage, and first/last train service references.
  • Seed reviewed CDT and LTI station timing data, including one SBS Transit sanity-check slice, and update the active plans to match the implemented shape.

Validation

  • npm run test:core
  • npm run test:fs
  • npm run data:validate
  • npm run check

Note: Turbo printed its local IO error: Operation not permitted warning during some runs, but all validation commands exited successfully.

Summary by CodeRabbit

  • New Features

    • Station records now include per-service first/last train times (weekday/saturday/sunday & public holidays) and an optional station layout with platforms and platform→service mappings.
  • Documentation

    • Updated data plan and seeding guidance to the new services/times shape and revised seeding order for layouts.
  • Tests

    • Tests added/updated to validate the new timing schema, special calendars, layout structure, and duplicate/coverage checks.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

Warning

Review limit reached

@duncanleo, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 53 minutes and 14 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c9ed2dd7-853f-4d78-b61e-b401442ca7f4

📥 Commits

Reviewing files that changed from the base of the PR and between 7dd70b3 and e36a75b.

📒 Files selected for processing (7)
  • data/station/CDT.json
  • docs/plans/active/station-first-last-train-data.md
  • docs/plans/active/station-layout-data.md
  • packages/core/src/schema/Station.test.ts
  • packages/core/src/schema/Station.ts
  • packages/fs/src/index.test.ts
  • packages/fs/src/validate.ts
📝 Walkthrough

Walkthrough

Restructures station firstLastTrain from calendar rows to per-service timed windows, adds a StationLayout schema, updates validation/tests/fixtures to the new shape, and seeds CDT and LTI station data.

Changes

Station Data Schema & Layout Expansion

Layer / File(s) Summary
Station schema: firstLastTrain & layout definitions
packages/core/src/schema/Station.ts
firstLastTrain restructures from entries[] to services[] where each service nests timing data under calendar-keyed times and specialTimes objects. New layout schema introduces platforms, levels, exits, access points, and transfer paths with comprehensive enums and validation constraints.
Schema validation tests: firstLastTrain & layout
packages/core/src/schema/Station.test.ts
Unit tests verify the new nested services structure for nullable first/last times, layout data acceptance with platforms and access points, and error paths aligned with the new nesting.
Validation: duplicate detection & service/layout checks
packages/fs/src/validate.ts
Adds validateDuplicateValue helper for detecting duplicates in layout IDs and platform access points. Refactors validateStationReferences to iterate firstLastTrain.services, deduplicate by serviceId, and validate services exist in layout platforms and current service revisions.
Validation integration tests: firstLastTrain & layout
packages/fs/src/index.test.ts
Updates test fixtures to use the new services[] structure with per-day times and exercises duplicate service detection, layout relationship validation, and revised error message paths.
Fixture generation: KET station with new schema
scripts/generate-fixtures.mjs
Updates test fixture generator to emit firstLastTrain.services[] with nested times keyed by calendar type instead of flat entries.
Real station data: CDT and LTI firstLastTrain & layout
data/station/CDT.json, data/station/LTI.json
Seed CDT with firstLastTrain services and layout (platforms mapped to CCL/TEL services, empty levels/exits/transferPaths). Seed LTI with firstLastTrain services for NEL and DTL lines.
Planning docs: schema, validation, and seeding strategy
docs/plans/active/station-first-last-train-data.md, docs/plans/active/station-layout-data.md
Defines new firstLastTrain.services model with examples, validates at least one of first/last per service and at least one of times/specialTimes, lists calendar and special categories, and adjusts layout seeding to start with CDT platform-to-service mappings before later PRs add levels/exits.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • foldaway/mrtdown-data#277: The active plan docs for station-first-last-train-data.md and station-layout-data.md were initially introduced in PR #277 and are now being updated here to reflect the finalized schema and validation structure.

Poem

🐰 I hopped through JSON fields anew,

services nest where calendars flew,
Platforms mapped in tidy rows,
CDT and LTI now show,
A cheerful rabbit humbly bows.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add station first-last train schema' is directly related to the main change—adding a station first-last train schema. However, it partially understates the scope since the PR also introduces station layout schema support and seeds station data.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/station-first-last-layout-data

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@duncanleo duncanleo changed the title [codex] Add station first/last train schema feat: add station first-last train schema Jun 1, 2026
@duncanleo duncanleo marked this pull request as ready for review June 1, 2026 03:19
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/core/src/schema/Station.test.ts (1)

72-127: ⚡ Quick win

Add a test for the empty-service timing refine.

These cases cover invalid timing values, but they do not assert that a service with neither times nor specialTimes is rejected. That is a new contract in StationFirstLastTrainServiceSchema, so it is worth pinning with a dedicated unit test.

Suggested test
   it('rejects timing rows without a first or last train time', () => {
     const result = StationSchema.safeParse({
       ...minimalStation(),
       firstLastTrain: {
         services: [
           {
             serviceId: 'ISL_MAIN_E',
             times: {
               weekday: {
                 firstTrain: null,
                 lastTrain: null,
               },
             },
           },
         ],
       },
     });

     expect(result.success).toBe(false);
     expect(result.error?.issues[0]?.message).toBe(
       'At least one of firstTrain or lastTrain must be set',
     );
   });
+
+  it('rejects services without times or specialTimes', () => {
+    const result = StationSchema.safeParse({
+      ...minimalStation(),
+      firstLastTrain: {
+        services: [{ serviceId: 'ISL_MAIN_E' }],
+      },
+    });
+
+    expect(result.success).toBe(false);
+    expect(result.error?.issues[0]?.message).toBe(
+      'At least one of times or specialTimes must be set',
+    );
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/schema/Station.test.ts` around lines 72 - 127, Add a new
unit test in Station.test.ts that constructs a station using
StationSchema.safeParse with a firstLastTrain.services entry that has a
serviceId (e.g., 'ISL_MAIN_E') but omits both times and specialTimes (or sets
them to undefined/null), then assert result.success is false and that the
validation errors include the path for that service (e.g.,
'firstLastTrain.services.0' or the specific refine error from
StationFirstLastTrainServiceSchema). This pins the new contract in
StationFirstLastTrainServiceSchema that a service must provide at least one of
times or specialTimes.
scripts/generate-fixtures.mjs (1)

299-321: ⚡ Quick win

Seed one specialTimes example in the generated fixture.

This generator covers the new times shape, but it still never emits specialTimes. Since recurring special calendars are part of the new contract, adding one eve_public_holiday sample here would give fixture-based validation an end-to-end path through that branch too.

Suggested fixture update
         services: [
           {
             serviceId: 'ISL_MAIN_E',
             times: {
               weekday: {
                 firstTrain: '06:00',
                 lastTrain: '00:50',
               },
               saturday: {
                 firstTrain: '06:05',
                 lastTrain: null,
               },
             },
+            specialTimes: {
+              eve_public_holiday: {
+                firstTrain: null,
+                lastTrain: '01:05',
+              },
+            },
           },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/generate-fixtures.mjs` around lines 299 - 321, Add a seeded
specialTimes example into the generated fixture so the new
recurring-special-calendar branch is exercised: inside the services array, pick
one service object (e.g., the one with serviceId 'ISL_MAIN_E') and add a
times.specialTimes entry containing a sample recurring key 'eve_public_holiday'
with a small times object (e.g., firstTrain/lastTrain similar to weekday or
saturday) so generated fixtures emit at least one specialTimes example alongside
the existing times shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@data/station/CDT.json`:
- Around line 34-88: The CDT.json firstLastTrain.services array is missing
entries for the CCW/extension serviceIds referenced by platforms CCDT_A and
CCDT_B (CCL_MAIN_CCW, CCL_EXT_CCW, CCL_EXT_CW), causing a mismatch; either add
corresponding timing objects for those serviceIds to firstLastTrain.services
(using the times from the matching data/service/*.json entries) or remove those
serviceIds from the CCDT_A/CCDT_B platform lists if they should not have
first/last timings—update the firstLastTrain.services array to include objects
with serviceId fields exactly matching CCL_MAIN_CCW, CCL_EXT_CCW, and CCL_EXT_CW
and appropriate weekday/saturday/sunday_public_holiday time blocks.

In `@packages/core/src/schema/Station.ts`:
- Around line 176-183: The StationLayoutPlatformSchema currently allows an empty
serviceIds array which breaks downstream coverage checks; update the serviceIds
field in StationLayoutPlatformSchema to require at least one entry (e.g., change
serviceIds: z.array(z.string()) to a non-empty array such as
z.array(z.string()).min(1) or z.array(z.string()).nonempty()) so every platform
must include at least one serviceId for correct line/service mapping.

---

Nitpick comments:
In `@packages/core/src/schema/Station.test.ts`:
- Around line 72-127: Add a new unit test in Station.test.ts that constructs a
station using StationSchema.safeParse with a firstLastTrain.services entry that
has a serviceId (e.g., 'ISL_MAIN_E') but omits both times and specialTimes (or
sets them to undefined/null), then assert result.success is false and that the
validation errors include the path for that service (e.g.,
'firstLastTrain.services.0' or the specific refine error from
StationFirstLastTrainServiceSchema). This pins the new contract in
StationFirstLastTrainServiceSchema that a service must provide at least one of
times or specialTimes.

In `@scripts/generate-fixtures.mjs`:
- Around line 299-321: Add a seeded specialTimes example into the generated
fixture so the new recurring-special-calendar branch is exercised: inside the
services array, pick one service object (e.g., the one with serviceId
'ISL_MAIN_E') and add a times.specialTimes entry containing a sample recurring
key 'eve_public_holiday' with a small times object (e.g., firstTrain/lastTrain
similar to weekday or saturday) so generated fixtures emit at least one
specialTimes example alongside the existing times shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b832af4e-abc9-4055-863b-65c4ea8955e7

📥 Commits

Reviewing files that changed from the base of the PR and between 1325e2e and 7a7cb20.

📒 Files selected for processing (9)
  • data/station/CDT.json
  • data/station/LTI.json
  • docs/plans/active/station-first-last-train-data.md
  • docs/plans/active/station-layout-data.md
  • packages/core/src/schema/Station.test.ts
  • packages/core/src/schema/Station.ts
  • packages/fs/src/index.test.ts
  • packages/fs/src/validate.ts
  • scripts/generate-fixtures.mjs

Comment thread data/station/CDT.json
Comment thread packages/core/src/schema/Station.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7a7cb204e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread data/station/CDT.json
Comment thread data/station/CDT.json Outdated
Comment thread packages/fs/src/validate.ts
@duncanleo duncanleo force-pushed the codex/station-first-last-layout-data branch from 3a38f9c to 5a8a347 Compare June 1, 2026 04:43
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5a8a3473ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread data/station/CDT.json Outdated
@duncanleo duncanleo force-pushed the codex/station-first-last-layout-data branch from 5a8a347 to 52addaa Compare June 1, 2026 07:43
@foldaway foldaway deleted a comment from chatgpt-codex-connector Bot Jun 1, 2026
@duncanleo duncanleo force-pushed the codex/station-first-last-layout-data branch 2 times, most recently from 7dd70b3 to 00bc9dc Compare June 1, 2026 09:12
@duncanleo duncanleo force-pushed the codex/station-first-last-layout-data branch from 00bc9dc to e36a75b Compare June 1, 2026 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant