Proof
Every network number is computed twice, by two different programs.
FalkorDB does the work: the agents' tools run Cypher and graph algorithms on your copy of the subway graph. A separate Python program built on NetworkX reads the same data files, follows the same written spec, and recomputes the answers. Every max flow also carries a certificate that proves it maximal. Where a number would differ, the checks that compare them stop with an error: pnpm judge re-measures every README number on a FalkorDB and fails on any mismatch. Memory, the agent and the brain cannot be recomputed that way, so they are measured against baselines instead, with their caveats beside them.
The entry submitted before the deadline is v1.0.0. Checks 2 to 7 were added after the deadline, on October 19.
Check 1
FalkorDB and NetworkX agree on every closure.
Max flow from a super source to a super sink over spare capacity, per direction of travel. FalkorDB runs algo.maxFlow on a forked graph; the checker runs NetworkX's Edmonds-Karp on the same network. Both follow docs/model.md.
| Closure | Direction | To move · FalkorDB | NetworkX | Max flow · FalkorDB | NetworkX | Agree |
|---|---|---|---|---|---|---|
| L train: Canarsie Tube closed | forward | 18,432 | 18432.4 | 17,167 | 17166.7 | ✓ yes |
| L train: Canarsie Tube closed | backward | 2,423 | 2423.1 | 2,033 | 2033.4 | ✓ yes |
| Lexington Ave express suspended | forward | 15,022 | 15021.7 | 15,022 | 15021.7 | ✓ yes |
| Lexington Ave express suspended | backward | 14,487 | 14486.6 | 14,487 | 14486.6 | ✓ yes |
| A and C: Cranberry Street Tunnel closed | forward | 14,267 | 14267.5 | 14,267 | 14267.5 | ✓ yes |
| A and C: Cranberry Street Tunnel closed | backward | 2,001 | 2000.6 | 2,001 | 2000.6 | ✓ yes |
Riders hit, by stops from the L closure
The affected_riders tool walks upstream one hop at a time in Cypher and joins the 2025 origin-destination flows whose paths cross the closure. NetworkX counts the same thing by breadth-first search.
| Within | FalkorDB | NetworkX | Agree |
|---|---|---|---|
| boards in the closed stretch | 7,823 | 7823.2 | ✓ |
| 1 stop | 10,142 | 10142.4 | ✓ |
| 2 stops | 12,292 | 12291.6 | ✓ |
| 3 stops | 13,400 | 13399.8 | ✓ |
| 4 stops | 14,713 | 14713.0 | ✓ |
| 5 stops | 15,975 | 15975.3 | ✓ |
| 6 stops | 17,272 | 17272.3 | ✓ |
How the check runs
- Same inputs. Both read
data/nyc/network.jsonanddemand.json, built from MTA files byscripts/build-network.ts. - Different code. FalkorDB tools in TypeScript and Cypher; verify.py in Python with NetworkX 3.7, which also checks that the min cut's capacity equals the max flow.
- Run by hand, not yet in CI. run-scenarios.ts seeds FalkorDB, runs every tool on all three closures and exits with an error when a number drifts from NetworkX by more than rounding. A GitHub workflow for it is written but not switched on, so each run is started by hand before results are committed.
- Where the min cut sits. On the L, the min cut holds no open segment: the open network has room everywhere the riders can reach. Only stations the closure cuts off limit the flow, 1 Av as an origin and 3 Av as a destination, both served only by the L and with no walking link. That is why the recommended shuttle starts at 1 Av.
Check 2
Every max flow is certified before anyone reads it.
FalkorDB's algo.maxFlow is fast but not always right, so its answer is never taken on its word. After each call the tool reads back the flows and checks three things: every edge within capacity and flow conserved at every station; no augmenting path left in the residual graph; and the value equal to the capacity of the cut it leaves. If any check fails, Detour's own Dinic solver recomputes the flow on the same edges and the tool says so in its result. Code in src/lib/graph/flow.ts.
| Closure | Direction | Max flow | Cut capacity | Answer from | Headroom | What limits it |
|---|---|---|---|---|---|---|
| L train: Canarsie Tube closed | forward | 17,166.7 | 17,166.7 | ✓ FalkorDB, certified | 1.77× | No open segment; only cut-off stations: 1 Av (origin), 3 Av (destination) |
| L train: Canarsie Tube closed | backward | 2,033.4 | 2,033.4 | ✓ FalkorDB, certified | > 20× | No open segment; only cut-off stations: 1 Av (destination), 3 Av (destination) |
| Lexington Ave express suspended | forward | 15,021.7 | 15,021.7 | ✓ FalkorDB, certified | 10.61× | Nothing today; first to fill: J 111 St → Woodhaven Blvd |
| Lexington Ave express suspended | backward | 14,486.6 | 14,486.6 | ✓ FalkorDB, certified | 11.14× | Nothing today; first to fill: G Greenpoint Av → 21 St |
| A and C: Cranberry Street Tunnel closed | forward | 14,267.5 | 14,267.5 | ✓ FalkorDB, certified | 7.12× | Nothing today; first to fill: FS Park Pl → Franklin Av-Medgar Evers College/Botanic Garden |
| A and C: Cranberry Street Tunnel closed | backward | 2,000.6 | 2,000.6 | ✓ FalkorDB, certified | > 20× | Nothing within 20× today's demand |
Why the check is there
Another Graph Hacks team, Chokepoint, found algo.maxFlow returning wrong answers on node-split networks and published an 11-edge reproduction. On FalkorDB 4.22.1, the engine Detour runs on, it returns 300 where the maximum is 453, with flows that are not even conserved. The certificate rejects it. On 100 random node-split networks FalkorDB returned a value below the maximum or a flow that breaks conservation 13 times; the certificate caught 13 and let none through.
scripts/judge/maxflow-repro.ts · pnpm judge:maxflow
On Detour's own networks
The defect has not fired on the subway: 90 of 90 max flows on 48 held-out closures and all 254 headroom solves passed the certificate as FalkorDB returned them (0 needed Dinic). Headroom is how far displaced demand can grow before an open segment saturates: an exact parametric max flow, a few certified solves on the fork per direction.
docs/model.md, “Checking max flow” and “Headroom”
Check 3
48 closures nobody tuned for: the two programs still agree.
The three demo closures could have been fitted by hand. These were not: 48 stretches of 2 to 6 segments on 25 lines, drawn with a fixed seed (20261019), each closed on FalkorDB through the controller's own procedure and recomputed by the NetworkX verifier.
FalkorDB against NetworkX
| Measure | Agree | Rate |
|---|---|---|
| Closed segments | 48 of 48 | 100% |
| Displaced riders | 48 of 48 | 100% |
| Max flow, each direction | 48 of 48 | 100% |
| Max flow certified | 48 of 48 | 100% |
| Cut-off stations | 48 of 48 | 100% |
| Headroom multiplier | 48 of 48 | 100% |
| Riders with no path | 48 of 48 | 100% |
| Fastest-path overflow | 48 of 48 | 100% |
Rider counts within 1.5 an hour; closed segments and cut-off stations identical; headroom within 0.1%. Overflow: the same value on 42 closures. On 9 the fastest path is not unique, two routes take the same minutes, and which one each program picks moves the overflow (by up to 1,248 riders an hour on 7: 42 St-Bryant Pk/5 Av to 33 St-Rawson St); there it is checked against the range NetworkX finds across 8 random tie-breaks, and lies inside it on 6 more.
The controller's plan against an oracle
15 of the 48 closures leave riders with no path or a max-flow shortfall, so the controller tests shuttles. For each, an oracle simulated every candidate leg the controller saw on its own fork and kept the best gain per bus (riders with no path plus shortfall cleared, per bus on the road).
- Best choice
- 11 of 15
- Mean gap
- 4%
- Worst gap
- 33%
- Q: 72 St to 57 St-7 Av: the controller cleared 93 riders per bus with 12 buses; the best single leg clears 140 with 8.
- 7: Court Sq-23 St to Jackson Hts-Roosevelt Av/74 St-Broadway: the controller cleared 326 riders per bus with 22 buses; the best single leg clears 402 with 6.
- L: Grand St to DeKalb Av: the controller cleared 354 riders per bus with 5 buses; the best single leg clears 368 with 4.
- The controller simulates only the first candidate for riders with no line and the first for relief; the oracle tries them all.
scripts/eval/closures-eval.ts · data/eval/closures.json · FalkorDB 4.22.1
Check 4
What the verifier recomputes, and what it cannot.
Beyond max flow, the NetworkX program rebuilds the platform layer with the same costs (3 minutes and half the headway to board, the ride, a free exit, walks) and sends every displaced rider down their fastest open path, so the overloaded G, the riders with no path and the shuttle's effect are recomputed too. 8 of 8 L-closure numbers below match. The last rows have no second implementation, and say so.
| L closure | FalkorDB | NetworkX | Agree |
|---|---|---|---|
| Displaced riders | 23,445 | 23,445 | ✓ yes |
| Overloaded segments on fastest paths (%) | 190, 198, 201, 244 | 190, 198, 201, 244 | ✓ yes |
| Riders with no open path | 4,689 | 4,689 | ✓ yes |
| Fastest-path overflow | 24,078 | 24,078 | ✓ yes |
| Cut-off stations toward Manhattan | 1 Av (origin), 3 Av (destination) | 1 Av (origin), 3 Av (destination) | ✓ yes |
| Headroom toward Manhattan | 1.77× | 1.77× | ✓ yes |
| Shuttle 1 Av ↔ Astor Pl: riders with no path after | 623 | 623 | ✓ yes |
| Shuttle 1 Av ↔ Astor Pl: max-flow shortfall after | 315 | 315 | ✓ yes |
| Memory: past shuttle carried 79%, so 27% more buses | read from graph memory | not recomputed | n/a |
| Concurrency: reads and writes a second | 7,195 reads/s | not recomputed | n/a |
| Step-free routes for remembered riders | k fastest paths, filtered | not recomputed | n/a |
Every README number, with the script and test behind it: CLAIMS.md. One command re-measures them all on a FalkorDB and fails on any difference: pnpm judge.
Check 5
With memory, a step-free rider gets the working elevator 66% of the time. Without it, 50%.
Every check below runs twice, and only memory changes between the two runs. The first replays real MTA elevator and escalator records month by month, so memory never sees a record from after the decision it informs; a placebo memory that keeps every record forever shows what Detour's validity windows and SUPERSEDES add. The rest run on FalkorDB with Detour's own tools.
A step-free rider can use one of two elevators (or escalators) at the same station. Which one is more likely to be running this month?
- No memorya coin picks · 95% interval 49.4% to 51.0%50.2%
- Placebo memoryevery record kept forever, no validity windows · 95% interval 57.3% to 66.8%61.8%
- Detour's memoryrecords valid for a year, superseded after an overhaul · 95% interval 61.3% to 72.4%66.3%
- Decisions
- 9,360
- 115 station complexes, 8 months held out
- Over no memory
- +16.1 pts
- 95% interval +10.9 to +22.4
- Over the placebo
- +4.5 pts
- 95% interval +1.5 to +7.7
Where memory falls short
- It still picks the wrong unit in 34% of decisions (3,153). Memory predicts from the past; a unit that breaks for the first time, or one just repaired, still fools it. These are its largest misses.
- Listing the 50 units that will break down most often in the months held out: 60% of Detour's list was right, against 10% by chance, but the placebo's all-time record did better, 72%. For chronic failures a long memory helps, and the one-year window throws that away.
- Elevators alone: 63.4% with memory, 50.3% without (3,657 decisions); escalators: 68.2%.
The validity window, varied
Detour keeps a unit's monthly record for twelve months. Windows from three months to two years land within a point of it, so the result does not hang on that choice.
| Window | Right |
|---|---|
| 3 months | 66.3% |
| 6 months | 67.0% |
| 12 months | 66.3% |
| 24 months | 66.7% |
How it was measured
- Source: MTA NYCT Subway Elevator and Escalator Availability: Beginning 2015 (Metropolitan Transportation Authority, via data.ny.gov), retrieved 2026-10-19; 698 units, 140 months from Jan 2015.
- Replay in time order. Each month's records reach memory only at the start of the next month; every decision reads only records valid before it (a check that throws on any later record found none).
- Decisions: every month from Jan 2026 to Aug 2026, every pair of units of one kind at one station complex whose availability that month differs by more than half a point. Right means memory picked the unit that ran more.
- No memory: a seeded coin. Placebo: every record ever read, kept forever (no validity window, no SUPERSEDES). Time-aware (Detour): records valid for 12 months, and a unit back from two months mostly out of service supersedes its older records, the way a superseded Finding is closed.
- 95% intervals: 2000 seeded bootstrap resamples of whole station complexes.
On the graph, with memory switched off and on
Step-free routes past a broken elevator
A wheelchair user asks for a step-free route. An elevator on the way is out of service, and memory knows it.
- Memory off
- 100% through it
- Memory on
- 0% through it
56 trips at 40 platforms. With memory, 59% found another step-free route (median 6.3 more minutes) and 41% were told there is none.
How it was measured
- 40 accessible station-and-line platforms, taken at even steps from the 264 in the network (sorted by station and line); 0 already had an outage in memory and were skipped.
- For each: report_elevator writes the outage as a Finding, then two step-free trips to and from an accessible station 3 to 8 stops along the line are routed with route_rider, once with memory off (outages ignored) and once on. The outage is then closed by a report that the elevator works again.
- A trip counts when, with memory off, its route boards or leaves the train at the broken platform.
Bus bridges sized from what past shuttles carried
A bus bridge is planned for so many riders an hour. How close does what it carries come to that?
- Memory off
- off by 255 riders/h
- Memory on
- off by 107 riders/h
Mean error over 32 past incidents, left out one at a time; 95% interval for the gain 72 to 233. Bridges within 5% of plan: 28% without, 50% with. Fed only earlier incidents: off by 135. Worse with memory on the A/C (115 → 126) and the J/Z (35 → 121): buses there run close to plan, and sizing for the lowest share on record adds buses after one weak run.
How it was measured
- 32 past shuttle incidents: 2 seeded (the L and A/C bridges) and 30 synthetic, six on each of 5 corridors whose buses carry a known share of plan (L 79%, A/C 93%, G 72%, 7 86%, J/Z 100%) give or take 6 points, from a seeded generator.
- Leave one out: each incident is hidden, recall reads the rest from FalkorDB, and the bridge is sized the way the scripted controller sizes it: buses an hour divided by the lowest share of plan a recalled shuttle carried, when under 97%.
- Without memory the buses run at the raw estimate. Carried = buses × 70 riders × the share that incident's buses really achieved; the error is its distance from the riders planned.
- Online: the same rule fed only incidents that came earlier. 95% interval: seeded bootstrap over incidents.
A fact from one session, used in the next
Does a fact written in one session change what a later session answers?
- Memory off
- 0 of 38 change
- Memory on
- 4 of 38 change
Aisha before: “Aisha: about 33 min, 0 transfers, step-free: M Myrtle-Wyckoff Avs → 14 St/6 Av (11 stops). Skipped a faster path because memory says: Elevator to the L platform at Myrtle-Wyckoff Avs out of service; M platform elevator works.” After: “Aisha: about 24 min, 0 transfers, step-free: L Myrtle-Wyckoff Avs → 14 St/6 Av (12 stops).”
How it was measured
- Session A writes 6 facts through report_elevator: the L platform elevator at Myrtle-Wyckoff Avs works again (Aisha's), and outages at the busiest accessible stations (Grand Central-42 St 4, Times Sq-42 St/Port Authority Bus Terminal 1, Fulton St 2, 34 St-Herald Sq B, 34 St-Penn Station A).
- Session B, a later request with a fresh context and another agent, asks 38 questions: every remembered rider's commute and 30 seeded step-free trips between accessible stations. Each answer is compared with the same question asked before session A.
- Control: the same questions with memory off change nothing.
Reports that arrive at the same moment
Many reports about one elevator arrive at once. Does memory end with exactly one current fact?
- Memory off
- –
- Memory on
- 1 open fact, every time
10 writers: 1 open after the same report, 1 after mixed reports; 50 writers: 1 open after the same report, 1 after mixed reports; 100 writers: 1 open after the same report, 1 after mixed reports
How it was measured
- For 10, 50 and 100 writers: all report the same outage at once, then half report it fixed and half report it broken, all at once. Each writer is its own context; the open elevator facts for the platform are counted after each burst.
- remember() writes the new finding and closes the one it supersedes in one query, and FalkorDB runs one write at a time per graph.
Run Oct 18, 2026, seeded, so a rerun gives the same numbers. scripts/eval/memory-eval.ts
Check 6
With the graph, the agent answers 93% of the 28 held-out questions run so far. The same model without it: 7%.
144 questions with answers computed without FalkorDB: plain TypeScript over the MTA data for closures, trips and step-free access, the NetworkX verifier for max flow and min cut, and the seeded memory for dates. 101 are held out and 43 were for development; the split was fixed with seed 20261018 and committed before any run, and questions about one closure or trip never straddle it. About one in five should be declined: a name or a relation that is not in the graph.
Running since October 18, 2026 on the deployment, on a sandbox graph of its own, with the free models gemini-flash-lite-latest, gemini-2.5-flash and openai/gpt-oss-120b. The baseline gets the model the graph arm ended on, the same question, and no tools. The run is partial: 28 held-out questions have run with the graph and 28 without it, 27 of them on both. The rest wait for the free models’ daily quotas, which the eval shares with visitors and never takes the last of; 231 arms that met a spent quota never ran and are left out, not counted wrong. It ran in two parts: the free database restarted empty in between, so the second run skipped the questions the first had answered on every arm, and each question’s model arms come from one run. scripts/eval
- LLM with the graph
- 93%
- 26 of 28 held out · 95% CI 82%–100% · dev –
- Same model, no graph
- 7%
- 2 of 28 held out · 95% CI 0%–18% · dev –
- Rules only, no model
- 100%
- 101 of 101 held out · 95% CI 100%–100% · dev 98%
Paired on the same 27 held-out questions: with the graph minus without it, +85 points (95% CI +70 to +96); with the graph minus rules alone, −7 points (−18 to 0). Seeded bootstrap, 2,000 resamples.
| Held-out questions | n | LLM with the graph | Same model, no graph | Rules only, no model |
|---|---|---|---|---|
| Riders a closure hits | 33 | 94% 17/18 | 0% 0/18 | 100% 33/33 |
| Max flow | 6 | 83% 5/6 | 17% 1/6 | 100% 6/6 |
| Min cut | 1 | 100% 1/1 | 0% 0/1 | 100% 1/1 |
| Cut off by a closure | 6 | 100% 1/1 | 100% 1/1 | 100% 6/6 |
| Fastest trip | 14 | – | 0% 0/1 | 100% 14/14 |
| Step-free trips | 6 | 100% 1/1 | 0% 0/1 | 100% 6/6 |
| Lines at a station | 7 | 100% 1/1 | – | 100% 7/7 |
| Memory, with dates | 9 | – | – | 100% 9/9 |
| Not in the graph | 8 | – | – | 100% 8/8 |
| Unknown names | 11 | – | – | 100% 11/11 |
- Declines what is not in the graph (graph · baseline)
- –
- Wrongly declines an answerable question
- 0% · 0%
- Riders within 1 stop of a closure, then 4 (graph)
- 100% · 83%
- The same pairs without the graph
- 0% · 0%
- Called the tools a question needs, in a valid order
- 96% (27 of 28)
- Answers whose every figure a tool returned
- 95% (19 of 20)
A valid order means names were resolved before their ids were used and a line was closed before anything was measured on it. The rules-only arm is a router written against these question templates, which dev and held-out share, so it is an optimistic floor: what plain code on the same graph tools gets when it knows exactly how questions are worded. The model never writes Cypher in any arm; every tool call is recorded with its arguments, query time and rows.
Where we lost
No held-out question went right without the graph and wrong with it.
Check 7
The brain's graph tools answer 100.0% of 769 held-out questions; keyword retrieval 84.4%.
1,261 questions are generated from the committed snapshot of the MTA's records, and their answers are computed in plain code from the raw feed JSON, never through FalkorDB. 340 of them have no answer in the records (units, alerts, events and projects that do not exist, units with no outage or no published alternative, quarters the complaint data does not cover), so refusing is scored too. Whole stations, alerts, events and projects are held out by a fixed seed (20261019); the brain was tuned on the 492 dev questions, with one exception noted below.
Each question goes to three arms with no model in the loop: the brain's standard procedure (it picks a graph tool from the question's words and answers from the tool's facts), a CONTAINS search over every record's text with the answer extracted from the top hits, and the same graph tools holding only the newest feed state. Scores are exact match and set F1 with 95% bootstrap intervals.
- Graph tools
- 100.0%
- exact match, 95% CI 100.0% to 100.0% · F1 100.0%
- refused 100.0% of 222 unanswerable · wrongly refused 0.0% of 547
- Keyword retrieval
- 84.4%
- exact match, 95% CI 81.5% to 86.9% · F1 85.3%
- refused 97.7% of 222 unanswerable · wrongly refused 11.9% of 547
- Latest snapshot only
- 83.1%
- exact match, 95% CI 80.4% to 85.6% · F1 85.1%
- refused 100.0% of 222 unanswerable · wrongly refused 20.7% of 547
| Question type | Held out | Graph tools | Keyword retrieval | Latest snapshot only |
|---|---|---|---|---|
| Is a unit in service now | 105 (25 no answer) | 100% | 100% | 100% |
| Outage's estimated return | 62 (38 no answer) | 100% | 100% | 100% |
| The MTA's alternative route | 95 (42 no answer) | 100% | 100% | 100% |
| Who maintains a unit | 80 (18 no answer) | 100% | 100% | 100% |
| Lines an alert affects | 67 (12 no answer) | 100% | 100% | 100% |
| Stations an alert affects | 28 (11 no answer) | 100% | 39% | 100% |
| Units out at an alert's stations (3 hops) | 38 (5 no answer) | 100% | 13% | 100% |
| Units out at a station, by name | 39 (5 no answer) | 100% | 62% | 100% |
| Stations on a line with outages | 16 (5 no answer) | 100% | 6% | 100% |
| Outages begun since a time | 19 | 100% | 0% | 16% |
| Outages resolved since a time | 1 | 100% | 100% | 0% |
| Capital project stage | 18 (9 no answer) | 100% | 100% | 100% |
| Capital project milestone date | 37 (7 no answer) | 100% | 100% | 100% |
| Outage complaints per station and quarter | 72 (27 no answer) | 100% | 79% | 38% |
| Updates in an alert event | 50 (8 no answer) | 100% | 100% | 16% |
| An alert event's last update | 32 (6 no answer) | 100% | 100% | 19% |
| What a decision rests on (sample org) | 5 (2 no answer) | 100% | 40% | 100% |
| Who made a decision (sample org) | 5 (2 no answer) | 100% | 40% | 100% |
What the baselines miss
- Joins. Which stations an alert names, which of them have a unit out, which stations on a line have outages: the answer is spread over several records linked by edges, not written in any one of them.
- Time. Outages that began or left the feed since a moment, an alert event's chain of updates, complaints per quarter: a brain that keeps only the newest feed cannot answer them, and refuses.
- Where keyword search is enough. One-record facts named by an id (a unit's status, maintainer or alternative route, a project's stage) are found by CONTAINS too, so those rows tie.
Read it with care
- The questions are templates in several wordings over the same records the tools read, so a high score says the tools answer these forms, not open questions.
- The keyword arm is given the question's identifiers and parameters and an extraction rule per record format; a plainer retriever would score lower.
- Fixes made while building it: the planner's wordings for stations along a line came from dev misses; reading full station names before partial ones was fixed after a held-out run, which scored 99.4% before it.
Held-out questions, with every arm's answer
Is a unit in service now105
Is ES258X working right now?
Answerout of service
- ✓Graph tools: out of service
- ✓Keyword retrieval: out of service
- ✓Latest snapshot only: out of service
Is EL135 working right now?
Answerout of service
- ✓Graph tools: out of service
- ✓Keyword retrieval: out of service
- ✓Latest snapshot only: out of service
Is the escalator ES117 running at the moment?
Answerout of service
- ✓Graph tools: out of service
- ✓Keyword retrieval: out of service
- ✓Latest snapshot only: out of service
Outage's estimated return62
How long will escalator ES258X be out?
Answer2026-10-20 16:00
- ✓Graph tools: 2026-10-20 16:00
- ✓Keyword retrieval: 2026-10-20 16:00
- ✓Latest snapshot only: 2026-10-20 16:00
When will EL135 be fixed?
Answer2027-01-31 23:00
- ✓Graph tools: 2027-01-31 23:00
- ✓Keyword retrieval: 2027-01-31 23:00
- ✓Latest snapshot only: 2027-01-31 23:00
How long will escalator ES117 be out?
Answer2026-10-18 23:00
- ✓Graph tools: 2026-10-18 23:00
- ✓Keyword retrieval: 2026-10-18 23:00
- ✓Latest snapshot only: 2026-10-18 23:00
The MTA's alternative route95
If elevator EL277 is down, what route does the MTA suggest instead?
AnswerIf you are on the mezzanine: For downtown 1, proceed to the elevator at the other side of the mezzanine to reach the platform and use the e…
- ✓Graph tools: If you are on the mezzanine: For downtown 1, proceed to the elevator at the other side of the mezzanine to reach the platform and use the e…
- ✓Keyword retrieval: If you are on the mezzanine: For downtown 1, proceed to the elevator at the other side of the mezzanine to reach the platform and use the e…
- ✓Latest snapshot only: If you are on the mezzanine: For downtown 1, proceed to the elevator at the other side of the mezzanine to reach the platform and use the e…
What's the step-free alternative when EL622 is broken?
AnswerIf you are on the mezzanine: For downtown 1, proceed to the elevator at the other side of the mezzanine to reach the platform and use the e…
- ✓Graph tools: If you are on the mezzanine: For downtown 1, proceed to the elevator at the other side of the mezzanine to reach the platform and use the e…
- ✓Keyword retrieval: If you are on the mezzanine: For downtown 1, proceed to the elevator at the other side of the mezzanine to reach the platform and use the e…
- ✓Latest snapshot only: If you are on the mezzanine: For downtown 1, proceed to the elevator at the other side of the mezzanine to reach the platform and use the e…
What is the MTA's alternative route when elevator EL118 is out of service?
AnswerUse an adjacent elevator.
- ✓Graph tools: Use an adjacent elevator.
- ✓Keyword retrieval: Use an adjacent elevator.
- ✓Latest snapshot only: Use an adjacent elevator.
Who maintains a unit80
Who maintains elevator EL454?
AnswerNYCT
- ✓Graph tools: NYCT
- ✓Keyword retrieval: NYCT
- ✓Latest snapshot only: NYCT
Which organization maintains EL201?
AnswerNYCT
- ✓Graph tools: NYCT
- ✓Keyword retrieval: NYCT
- ✓Latest snapshot only: NYCT
Which organization maintains EL199?
AnswerNYCT
- ✓Graph tools: NYCT
- ✓Keyword retrieval: NYCT
- ✓Latest snapshot only: NYCT
Lines an alert affects67
What lines are hit by MTA alert lmm:planned_work:35560?
AnswerC
- ✓Graph tools: C
- ✓Keyword retrieval: C
- ✓Latest snapshot only: C
Which subway lines does MTA alert lmm:planned_work:34550 affect?
AnswerA
- ✓Graph tools: A
- ✓Keyword retrieval: A
- ✓Latest snapshot only: A
Alert lmm:planned_work:35718: which routes does it cover?
AnswerM
- ✓Graph tools: M
- ✓Keyword retrieval: M
- ✓Latest snapshot only: M
Stations an alert affects28
Where does MTA alert lmm:planned_work:35562 apply?
Answer167, 618, 165, 164, 611, 162
- ✓Graph tools: 167, 618, 165, 164, 611, 162
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: 167, 618, 165, 164, 611, 162
Where does MTA alert lmm:planned_work:35355 apply?
Answer181, 180, 179, 627, 177, 176, 175
- ✓Graph tools: 181, 180, 179, 627, 177, 176, 175
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: 181, 180, 179, 627, 177, 176, 175
Where does MTA alert lmm:planned_work:35563 apply?
Answer153, 154, 155, 156, 157, 158, 159, 160, 614, 162, 611, 164, 165, 618, 167, 168
- ✓Graph tools: 153, 154, 155, 156, 157, 158, 159, 160, 614, 162, 611, 164, 165, 618, 167, 168
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: 153, 154, 155, 156, 157, 158, 159, 160, 614, 162, 611, 164, 165, 618, 167, 168
Units out at an alert's stations (3 hops)38
Which stations named in MTA alert lmm:planned_work:35124 have an elevator or escalator out of service right now?
Answer306
- ✓Graph tools: 306
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: 306
Do any stations in alert lmm:planned_work:33366 have a broken elevator or escalator right now?
Answer276
- ✓Graph tools: 276
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: 276
Which stations named in MTA alert lmm:planned_work:34174 have an elevator or escalator out of service right now?
Answer612, 414, 628, 602
- ✓Graph tools: 612, 414, 628, 602
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: 612, 414, 628, 602
Units out at a station, by name39
57 St-7 Av: which elevators or escalators are down?
AnswerEL281
- ✓Graph tools: EL281
- ✗Keyword retrieval: none
- ✓Latest snapshot only: EL281
Which elevators and escalators at Coney Island-Stillwell Av are out of service right now?
AnswerEL701
- ✓Graph tools: EL701
- ✓Keyword retrieval: EL701
- ✓Latest snapshot only: EL701
Are any elevators or escalators broken at Livonia Av?
AnswerEL771
- ✓Graph tools: EL771
- ✓Keyword retrieval: EL771
- ✓Latest snapshot only: EL771
Stations on a line with outages16
Which stations on the Q have an elevator or escalator out of service right now?
Answer617, 9, 602
- ✓Graph tools: 602, 9, 617
- ✗Keyword retrieval: none
- ✓Latest snapshot only: 602, 9, 617
Which R stations have a broken elevator or escalator?
Answer9, 35, 602
- ✓Graph tools: 602, 9, 35
- ✗Keyword retrieval: none
- ✓Latest snapshot only: 602, 9, 35
Which stations on the W have an elevator or escalator out of service right now?
Answer9
- ✓Graph tools: 9
- ✗Keyword retrieval: none
- ✓Latest snapshot only: 9
Outages begun since a time19
What elevator or escalator outages have started since 2026-10-18 16:29?
AnswerES117
- ✓Graph tools: ES117
- ✗Keyword retrieval: none
- ✓Latest snapshot only: ES117
Which elevator and escalator outages began since 2026-10-18 16:06?
AnswerES117, ES310, ES411
- ✓Graph tools: ES117, ES310, ES411
- ✗Keyword retrieval: none
- ✓Latest snapshot only: ES117, ES310, ES411
Which elevator and escalator outages began since 2026-10-18 15:35?
AnswerES117, ES310, ES411, EL281, EL388
- ✓Graph tools: ES117, ES310, ES411, EL281, EL388
- ✗Keyword retrieval: EL388
- ✓Latest snapshot only: ES117, ES310, ES411, EL281, EL388
Outages resolved since a time1
Which outages left the feed since 2026-10-18 16:30?
AnswerEL427
- ✓Graph tools: EL427
- ✓Keyword retrieval: EL427
- ✗Latest snapshot only: none
Capital project stage18
Where does MTA capital project 11097 stand?
AnswerConstruction
- ✓Graph tools: Construction
- ✓Keyword retrieval: Construction
- ✓Latest snapshot only: Construction
Which stage has capital project 11134 reached?
AnswerConstruction
- ✓Graph tools: Construction
- ✓Keyword retrieval: Construction
- ✓Latest snapshot only: Construction
Where does MTA capital project 12040 stand?
AnswerConstruction
- ✓Graph tools: Construction
- ✓Keyword retrieval: Construction
- ✓Latest snapshot only: Construction
Capital project milestone date37
Capital project 12338: when was "Contract Solicitation"?
Answer2021-04-01
- ✓Graph tools: 2021-04-01
- ✓Keyword retrieval: 2021-04-01
- ✓Latest snapshot only: 2021-04-01
When did MTA capital project 12197 reach the milestone "Bid Opening Date"?
Answer2020-01-01
- ✓Graph tools: 2020-01-01
- ✓Keyword retrieval: 2020-01-01
- ✓Latest snapshot only: 2020-01-01
Capital project 11097: when was "Financial Commitment and Contract Award"?
Answer2022-12-01
- ✓Graph tools: 2022-12-01
- ✓Keyword retrieval: 2022-12-01
- ✓Latest snapshot only: 2022-12-01
Outage complaints per station and quarter72
How many outage complaints did riders make about 14 St-Union Sq in 2025 Q2?
Answer3
- ✓Graph tools: 3
- ✗Keyword retrieval: 1
- ✗Latest snapshot only: refused
In 2026 Q3, how many elevator or escalator complaints came in for Atlantic Av-Barclays Ctr?
Answer7
- ✓Graph tools: 7
- ✗Keyword retrieval: 4
- ✗Latest snapshot only: refused
How many outage complaints did riders make about Sheepshead Bay in 2026 Q3?
Answer2
- ✓Graph tools: 2
- ✓Keyword retrieval: 2
- ✗Latest snapshot only: refused
Updates in an alert event50
Count the updates to subway alert event 257127.
Answer2
- ✓Graph tools: 2
- ✓Keyword retrieval: 2
- ✗Latest snapshot only: refused
Count the updates to subway alert event 257535.
Answer2
- ✓Graph tools: 2
- ✓Keyword retrieval: 2
- ✗Latest snapshot only: refused
How many updates did MTA subway alert event 261553 have?
Answer5
- ✓Graph tools: 5
- ✓Keyword retrieval: 5
- ✗Latest snapshot only: refused
An alert event's last update32
What was the final update for alert event 261893?
AnswerDowntown 6/6X trains are running with delays after we addressed a mechanical problem on a train at 23 St-Baruch College.
- ✓Graph tools: Downtown 6/6X trains are running with delays after we addressed a mechanical problem on a train at 23 St-Baruch College.
- ✓Keyword retrieval: Downtown 6/6X trains are running with delays after we addressed a mechanical problem on a train at 23 St-Baruch College.
- ✗Latest snapshot only: refused
How did MTA alert event 257802 end?
AnswerUptown R/W trains are running with delays after we removed a train with a mechanical problem from service at Whitehall St-South Ferry.
- ✓Graph tools: Uptown R/W trains are running with delays after we removed a train with a mechanical problem from service at Whitehall St-South Ferry.
- ✓Keyword retrieval: Uptown R/W trains are running with delays after we removed a train with a mechanical problem from service at Whitehall St-South Ferry.
- ✗Latest snapshot only: refused
What was the final update for alert event 259711?
AnswerUptown 4/6 trains are running with delays after a rail replacement at 96 St concluded.
- ✓Graph tools: Uptown 4/6 trains are running with delays after a rail replacement at 96 St concluded.
- ✓Keyword retrieval: Uptown 4/6 trains are running with delays after a rail replacement at 96 St concluded.
- ✗Latest snapshot only: refused
What a decision rests on (sample org)5
What is decision DEC-0616-SIZE based on?
AnswerDOC-AAR-0612, MSG-0612-3, BUS-0612-07, F-0612-1
- ✓Graph tools: F-0612-1, DOC-AAR-0612, BUS-0612-07, MSG-0612-3
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: F-0612-1, DOC-AAR-0612, BUS-0612-07, MSG-0612-3
What is decision DEC-0620-BUSES based on?
AnswerDEC-0616-SIZE, F-0612-1, MSG-0612-3
- ✓Graph tools: DEC-0616-SIZE, MSG-0612-3, F-0612-1
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: DEC-0616-SIZE, MSG-0612-3, F-0612-1
What does decision DEC-1014-SF rest on?
AnswerEE-1207, DOC-POLICY-SF, F-ELEV-2
- ✓Graph tools: F-ELEV-2, DOC-POLICY-SF, EE-1207
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: F-ELEV-2, DOC-POLICY-SF, EE-1207
Who made a decision (sample org)5
Whose decision was DEC-0616-SIZE?
AnswerRenee Alvarez
- ✓Graph tools: Renee Alvarez
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: Renee Alvarez
Who decided DEC-0620-BUSES?
AnswerTunde Okafor
- ✓Graph tools: Tunde Okafor
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: Tunde Okafor
Who made decision DEC-1014-SF?
AnswerJordan Lee
- ✓Graph tools: Jordan Lee
- ✗Keyword retrieval: refused
- ✓Latest snapshot only: Jordan Lee
Snapshot: outage feed read 4 times, last Oct 18, 2026, 4:42 PM ET; run Oct 18, 2026, 4:43 PM ET. Every held-out question, its answer and each arm's answer are in data/eval/brain.json; scripts/eval/brain-eval.ts reproduces it on any FalkorDB.
Check 8
7,195 reads and 719 writes a second, no leaks between tenants.
Every visitor gets a graph of their own. The benchmark runs 48 reader agents and 6 writer agents for 15 seconds across 6 tenant graphs over 12 connections, then checks that a closure written in one tenant is invisible in the others.
- Reads
- 107,961
- p50 5.4 ms · p95 15.5 ms
- Writes
- 10,795
- p50 7.4 ms · p95 17.1 ms
- Errors
- 0
- across both
- Isolation
- Held
- closed segments per tenant: 10, 0, 0, 0, 0, 0
FalkorDB 4.22.1 module on Redis 8.0.5, Vercel Sandbox, 4 vCPU. scripts/ci/bench.ts
Check 9
What each tool costs on the L closure.
Summed query time per call, from the same run. The expensive tools fork the graph, rebuild the flow network and reroute two thousand trips.
- Resolve names1 ms · 2q
- Resolve names1 ms · 2q
- Close the line47 ms · 7q
- Who rides through it246 ms · 10q
- Recall memory6 ms · 3q
- Measure capacity1.3 s · 21q
- Measure headroom971 ms · 31q
- Find shuttle bridges402 ms · 16q
- Simulate a plan2.1 s · 24q
- Simulate a plan2.2 s · 24q
- Publish the plan43 ms · 8q
- Steer riders, no buses2 ms · 3q
- Remembered riders hit3 ms · 2q
- Route a rideraisha33 ms · 5q
- Route a riderdev9 ms · 5q
- Route a ridermaya9 ms · 5q
- Route a ridertomas8 ms · 5q
- Route a rideraisha-stepfree21 ms · 5q
- Route a riderstaten-island5 ms · 3q
- Remember a finding3 ms · 2q
- Remember a finding4 ms · 2q
- Resolve the incident60 ms · 12q
Inputs
The data, and what was assumed.
445 station complexes, 26 routes, 1,459 scheduled segments, 190 walking links and 30,205 origin-destination pairs carrying 404,040 of the 446,230 riders the MTA estimates for a weekday 8 a.m. hour.
- MTA GTFS static (subway)service_id Weekday, departures 08:00-08:59 · retrieved 2026-10-16
- MTA Subway Stationsretrieved 2026-10-16
- MTA Subway Stations and Complexesretrieved 2026-10-16
- MTA Subway Origin-Destination Ridership Estimate: 2025October 2025, Wednesday, hour 8 · retrieved 2026-10-16
- Peak hour
- 08:00-08:59 weekday
- Riders per car (A division / 60 ft / 75 ft)
- 110 / 145 / 175
- Transfer penalty
- 3 min + half the headway
- Walking links
- under 400 m at 75 m/min
- Shuttle bus
- 70 riders, 4 min + 3 min per km
- Smallest flow kept
- 2 riders an hour
Baseline paths come from an all-or-nothing assignment (fastest route by in-vehicle time, half-headway wait and transfer penalty). Riders and past incidents in memory are example data, labeled as such.
Every query
110 Cypher queries and graph commands behind 14 tools.
The agents never write Cypher. They pick from this fixed catalogue; each call returns its answer shape, a summary, and the exact queries below with their timings, which the control room shows step by step.
Resolve namesresolve_places1
resolve "…"on detour:sbx:<visitor>
CALL db.idx.fulltext.queryNodes('Station', $q) YIELD node, score RETURN node.id AS id, node.name AS name, node.label AS label, node.routes AS routes, node.ada AS ada, score ORDER BY score DESC LIMIT 6
Close the lineclose_line7
reopen closed segmentson detour:sbx:<visitor>
MATCH ()-[r:RUNS {closed: true}]->() SET r.closed = false, r.minutes = r.baseMinutes, r.spare = CASE WHEN r.capacity > r.load THEN r.capacity - r.load ELSE 0 END RETURN count(r) AS nreopen platform rideson detour:sbx:<visitor>
MATCH ()-[r:RIDE {closed: true}]->() SET r.closed = false, r.minutes = r.baseMinutesclear crowding costson detour:sbx:<visitor>
MATCH ()-[r:BOARD|ALIGHT|RIDE|WALK|BUS]->() WHERE r.cost IS NOT NULL SET r.cost = null, r.crowd = nullclose out incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) SET i.status = $status, i.closedMs = timestamp()close segmentson detour:sbx:<visitor>
MATCH (a:Station)-[r:RUNS]->(b:Station) WHERE r.route IN $routes AND a.id IN $stretch AND b.id IN $stretch SET r.closed = true, r.minutes = $closedMinutes, r.spare = 0 RETURN r.eid AS eid, r.route AS route, a.id AS from, b.id AS to, r.load AS load ORDER BY eidclose the same rides on the platform layeron detour:sbx:<visitor>
MATCH ()-[r:RIDE]->() WHERE r.eid IN $eids SET r.closed = true, r.minutes = $closedMinutesrecord incidenton detour:sbx:<visitor>
MATCH (ag:Agent {id: $agent}) CREATE (ag)-[:OPENED {atMs: timestamp()}]->(i:Incident {id: $id, title: $title, report: $report, routes: $routes, stretch: $stretch, closedEids: $closedEids, status: 'open', openedMs: timestamp()}) WITH i MATCH (s:Station) WHERE s.id IN $stretch CREATE (i)-[:CLOSES]->(s)
Who rides through itaffected_riders3
open incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) RETURN i.id AS id, i.title AS title, i.report AS report, i.routes AS routes, i.stretch AS stretch, i.closedEids AS closedEids, i.status AS status, i.openedMs AS openedMs ORDER BY i.openedMs DESC LIMIT 1displaced riderson detour:demand
MATCH (o:Station)-[d:DEMAND]->(x:Station) WHERE any(e IN d.via WHERE e IN $closed) RETURN o.id AS o, x.id AS d, d.riders AS riders, d.via AS viastations k stops upstreamon detour:sbx:<visitor>
MATCH (o:Station)-[:RUNS]->(t:Station) WHERE t.id IN $frontier AND NOT o.id IN $seen RETURN DISTINCT o.id AS id
Recall memoryrecall3
findings about these stations and routeson detour:sbx:<visitor>
MATCH (f:Finding)-[:ABOUT]->(n) WHERE (n:Station AND n.id IN $stations) OR (n:Route AND n.id IN $routes) WITH DISTINCT f OPTIONAL MATCH (f)-[:FROM]->(i:Incident) OPTIONAL MATCH (ag:Agent)-[:WROTE]->(f) RETURN f.id AS id, f.text AS text, f.kind AS kind, f.confidence AS confidence, f.validFromMs AS validFromMs, f.validToMs AS validToMs, f.example AS example, i.id AS incident, ag.name AS agent ORDER BY validFromMs DESC LIMIT 12past incidents on these routeson detour:sbx:<visitor>
MATCH (i:Incident) WHERE i.status <> 'open' AND any(r IN i.routes WHERE r IN $routes) OPTIONAL MATCH (s:Shuttle)-[:FOR]->(i) OPTIONAL MATCH (s)-[:FROM]->(a:Station) OPTIONAL MATCH (s)-[:TO]->(b:Station) WITH i, collect(CASE WHEN s IS NULL THEN null ELSE {from: a.name, to: b.name, bph: s.busesPerHour, planned: s.planned, observed: s.observed} END) AS shuttles RETURN i.id AS id, i.title AS title, i.openedMs AS openedMs, i.example AS example, shuttles ORDER BY openedMs DESC LIMIT 5playbooks for these routeson detour:sbx:<visitor>
MATCH (p:Playbook)-[:FOR]->(r:Route) WHERE r.id IN $routes RETURN DISTINCT p.id AS id, p.title AS title, p.steps AS steps, p.uses AS uses, p.lastUsedMs AS lastUsedMs
Measure capacitymeasure_capacity17
open incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) RETURN i.id AS id, i.title AS title, i.report AS report, i.routes AS routes, i.stretch AS stretch, i.closedEids AS closedEids, i.status AS status, i.openedMs AS openedMs ORDER BY i.openedMs DESC LIMIT 1fork the sandboxon detour:fork:<visitor>:<fork>
GRAPH.COPY detour:sbx:<visitor> detour:fork:<visitor>:<fork>drop closed segments in the forkon detour:fork:<visitor>:<fork>
MATCH ()-[r:RUNS {closed: true}]->() DELETE rdrop closed rides in the forkon detour:fork:<visitor>:<fork>
MATCH ()-[r:RIDE {closed: true}]->() DELETE rdisplaced riderson detour:demand
MATCH (o:Station)-[d:DEMAND]->(x:Station) WHERE any(e IN d.via WHERE e IN $closed) RETURN o.id AS o, x.id AS d, d.riders AS riders, d.via AS viafree displaced riders' capacityon detour:fork:<visitor>:<fork>
UNWIND $freed AS f MATCH ()-[r:RUNS {eid: f.eid}]->() SET r.freed = f.riders, r.spare = CASE WHEN r.capacity - r.load + f.riders > 0 THEN r.capacity - r.load + f.riders ELSE 0 ENDforward source and sinkon detour:fork:<visitor>:<fork>
CREATE (src:Hub {id: $src}), (snk:Hub {id: $snk}) WITH src, snk UNWIND $sources AS s MATCH (x:Station {id: s.id}) CREATE (src)-[:FLOW {spare: s.cap, kind: $kind, ref: -1}]->(x)forward sinkson detour:fork:<visitor>:<fork>
MATCH (snk:Hub {id: $snk}) UNWIND $sinks AS s MATCH (x:Station {id: s.id}) CREATE (x)-[:FLOW {spare: s.cap, kind: $kind, ref: -1}]->(snk)clear corridor flow edgeson detour:fork:<visitor>:<fork>
MATCH (:Station)-[f:FLOW]->(:Station) DELETE faggregate spare capacity by corridoron detour:fork:<visitor>:<fork>
MATCH (a:Station)-[r]->(b:Station) WHERE type(r) IN $kinds WITH a, b, sum(r.spare) AS spare, collect(CASE type(r) WHEN 'RUNS' THEN 'R' + toString(r.eid) WHEN 'WALK' THEN 'W' + toString(r.wid) ELSE 'B' + toString(r.bid) END) AS refs CREATE (a)-[:FLOW {spare: spare, kind: 'NET', refs: refs}]->(b)forward max flowon detour:fork:<visitor>:<fork>
MATCH (src:Hub {id: $src}), (snk:Hub {id: $snk}) CALL algo.maxFlow({sourceNodes: [src], targetNodes: [snk], relationshipTypes: ['FLOW'], capacityProperty: 'spare'}) YIELD maxFlow, edges, edgeFlows RETURN maxFlow, [e IN edges | [e.kind, startNode(e).id, endNode(e).id]] AS es, edgeFlows AS flowsforward residual graphon detour:fork:<visitor>:<fork>
MATCH (a)-[r:FLOW]->(b) WHERE r.kind IN $kinds RETURN r.kind AS kind, a.id AS a, b.id AS b, r.spare AS cap, r.refs AS refsreroute displaced riders on fastest open pathson detour:fork:<visitor>:<fork>
UNWIND $pairs AS p MATCH (a:Station {id: p.o}), (b:Station {id: p.d}) CALL algo.SPpaths({sourceNode: a, targetNode: b, relTypes: $types, weightProp: 'minutes'}) YIELD path, pathWeight RETURN p.o AS o, p.d AS d, pathWeight AS minutes, [r IN relationships(path) WHERE type(r) = 'RIDE' | r.eid] AS eids, [r IN relationships(path) WHERE type(r) = 'BUS' | [r.bid, startNode(r).id]] AS bids, [r IN relationships(path) WHERE type(r) = 'WALK' | r.wid] AS walksdrop the forkon detour:fork:<visitor>:<fork>
GRAPH.DELETE detour:fork:<visitor>:<fork>remember the capacity pictureon detour:sbx:<visitor>
MATCH (i:Incident {id: $id}) SET i.displaced = $displaced, i.overflow = $overflow, i.stranded = $stranded, i.required = $required, i.absorbed = $absorbed, i.unmet = $unmet, i.measuredMs = timestamp()routing cost is travel timeon detour:sbx:<visitor>
MATCH ()-[r:BOARD|ALIGHT|RIDE|WALK|BUS]->() SET r.cost = r.minutes, r.crowd = nullcrowded rides cost moreon detour:sbx:<visitor>
UNWIND $segs AS c MATCH ()-[r:RIDE {eid: c.eid}]->() SET r.crowd = c.pct, r.cost = r.minutes * c.factor RETURN count(r) AS n
Measure headroommeasure_headroom15
open incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) RETURN i.id AS id, i.title AS title, i.report AS report, i.routes AS routes, i.stretch AS stretch, i.closedEids AS closedEids, i.status AS status, i.openedMs AS openedMs ORDER BY i.openedMs DESC LIMIT 1fork the sandboxon detour:fork:<visitor>:<fork>
GRAPH.COPY detour:sbx:<visitor> detour:fork:<visitor>:<fork>drop closed segments in the forkon detour:fork:<visitor>:<fork>
MATCH ()-[r:RUNS {closed: true}]->() DELETE rdrop closed rides in the forkon detour:fork:<visitor>:<fork>
MATCH ()-[r:RIDE {closed: true}]->() DELETE rdisplaced riderson detour:demand
MATCH (o:Station)-[d:DEMAND]->(x:Station) WHERE any(e IN d.via WHERE e IN $closed) RETURN o.id AS o, x.id AS d, d.riders AS riders, d.via AS viafree displaced riders' capacityon detour:fork:<visitor>:<fork>
UNWIND $freed AS f MATCH ()-[r:RUNS {eid: f.eid}]->() SET r.freed = f.riders, r.spare = CASE WHEN r.capacity - r.load + f.riders > 0 THEN r.capacity - r.load + f.riders ELSE 0 ENDforward source and sinkon detour:fork:<visitor>:<fork>
CREATE (src:Hub {id: $src}), (snk:Hub {id: $snk}) WITH src, snk UNWIND $sources AS s MATCH (x:Station {id: s.id}) CREATE (src)-[:FLOW {spare: s.cap, kind: $kind, ref: -1}]->(x)forward sinkson detour:fork:<visitor>:<fork>
MATCH (snk:Hub {id: $snk}) UNWIND $sinks AS s MATCH (x:Station {id: s.id}) CREATE (x)-[:FLOW {spare: s.cap, kind: $kind, ref: -1}]->(snk)clear corridor flow edgeson detour:fork:<visitor>:<fork>
MATCH (:Station)-[f:FLOW]->(:Station) DELETE faggregate spare capacity by corridoron detour:fork:<visitor>:<fork>
MATCH (a:Station)-[r]->(b:Station) WHERE type(r) IN $kinds WITH a, b, sum(r.spare) AS spare, collect(CASE type(r) WHEN 'RUNS' THEN 'R' + toString(r.eid) WHEN 'WALK' THEN 'W' + toString(r.wid) ELSE 'B' + toString(r.bid) END) AS refs CREATE (a)-[:FLOW {spare: spare, kind: 'NET', refs: refs}]->(b)forward remember feed capacitieson detour:fork:<visitor>:<fork>
MATCH ()-[f:FLOW {kind: $feed}]->() SET f.base = f.spareforward max flow at 1× demandon detour:fork:<visitor>:<fork>
MATCH (src:Hub {id: $src}), (snk:Hub {id: $snk}) CALL algo.maxFlow({sourceNodes: [src], targetNodes: [snk], relationshipTypes: ['FLOW'], capacityProperty: 'spare'}) YIELD maxFlow, edges, edgeFlows RETURN maxFlow, [e IN edges | [e.kind, startNode(e).id, endNode(e).id]] AS es, edgeFlows AS flowsforward residual graphon detour:fork:<visitor>:<fork>
MATCH (a)-[r:FLOW]->(b) WHERE r.kind IN $kinds RETURN r.kind AS kind, a.id AS a, b.id AS b, r.spare AS cap, r.refs AS refsforward scale demand to 20.000×on detour:fork:<visitor>:<fork>
MATCH ()-[f:FLOW {kind: $feed}]->() SET f.spare = f.base * $lambdadrop the forkon detour:fork:<visitor>:<fork>
GRAPH.DELETE detour:fork:<visitor>:<fork>
Find shuttle bridgesshuttle_candidates14
open incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) RETURN i.id AS id, i.title AS title, i.report AS report, i.routes AS routes, i.stretch AS stretch, i.closedEids AS closedEids, i.status AS status, i.openedMs AS openedMs ORDER BY i.openedMs DESC LIMIT 1fork the sandboxon detour:fork:<visitor>:<fork>
GRAPH.COPY detour:sbx:<visitor> detour:fork:<visitor>:<fork>drop closed segments in the forkon detour:fork:<visitor>:<fork>
MATCH ()-[r:RUNS {closed: true}]->() DELETE rdrop closed rides in the forkon detour:fork:<visitor>:<fork>
MATCH ()-[r:RIDE {closed: true}]->() DELETE rdisplaced riderson detour:demand
MATCH (o:Station)-[d:DEMAND]->(x:Station) WHERE any(e IN d.via WHERE e IN $closed) RETURN o.id AS o, x.id AS d, d.riders AS riders, d.via AS viafree displaced riders' capacityon detour:fork:<visitor>:<fork>
UNWIND $freed AS f MATCH ()-[r:RUNS {eid: f.eid}]->() SET r.freed = f.riders, r.spare = CASE WHEN r.capacity - r.load + f.riders > 0 THEN r.capacity - r.load + f.riders ELSE 0 ENDforward source and sinkon detour:fork:<visitor>:<fork>
CREATE (src:Hub {id: $src}), (snk:Hub {id: $snk}) WITH src, snk UNWIND $sources AS s MATCH (x:Station {id: s.id}) CREATE (src)-[:FLOW {spare: s.cap, kind: $kind, ref: -1}]->(x)forward sinkson detour:fork:<visitor>:<fork>
MATCH (snk:Hub {id: $snk}) UNWIND $sinks AS s MATCH (x:Station {id: s.id}) CREATE (x)-[:FLOW {spare: s.cap, kind: $kind, ref: -1}]->(snk)clear corridor flow edgeson detour:fork:<visitor>:<fork>
MATCH (:Station)-[f:FLOW]->(:Station) DELETE faggregate spare capacity by corridoron detour:fork:<visitor>:<fork>
MATCH (a:Station)-[r]->(b:Station) WHERE type(r) IN $kinds WITH a, b, sum(r.spare) AS spare, collect(CASE type(r) WHEN 'RUNS' THEN 'R' + toString(r.eid) WHEN 'WALK' THEN 'W' + toString(r.wid) ELSE 'B' + toString(r.bid) END) AS refs CREATE (a)-[:FLOW {spare: spare, kind: 'NET', refs: refs}]->(b)forward max flowon detour:fork:<visitor>:<fork>
MATCH (src:Hub {id: $src}), (snk:Hub {id: $snk}) CALL algo.maxFlow({sourceNodes: [src], targetNodes: [snk], relationshipTypes: ['FLOW'], capacityProperty: 'spare'}) YIELD maxFlow, edges, edgeFlows RETURN maxFlow, [e IN edges | [e.kind, startNode(e).id, endNode(e).id]] AS es, edgeFlows AS flowsforward residual graphon detour:fork:<visitor>:<fork>
MATCH (a)-[r:FLOW]->(b) WHERE r.kind IN $kinds RETURN r.kind AS kind, a.id AS a, b.id AS b, r.spare AS cap, r.refs AS refsshuttle candidates within reachon detour:fork:<visitor>:<fork>
UNWIND $sources AS s MATCH (a:Station {id: s.id}), (b:Station) WHERE b.id <> a.id AND NOT b.id IN $stretch AND distance(a.loc, b.loc) <= $maxMeters WITH a, b, s, distance(a.loc, b.loc) AS meters MATCH (b)-[r:RUNS]->() WITH a, b, s, meters, sum(r.spare) AS outSpare, collect(DISTINCT r.route) AS lines WHERE size([x IN lines WHERE NOT x IN $closedRoutes]) > 0 RETURN a.id AS fromId, a.name AS fromName, b.id AS toId, b.name AS toName, lines, s.net AS supply, s.reason AS reason, meters, outSpare ORDER BY reason DESC, supply DESC, meters ASCdrop the forkon detour:fork:<visitor>:<fork>
GRAPH.DELETE detour:fork:<visitor>:<fork>
Simulate a plansimulate_plan17
open incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) RETURN i.id AS id, i.title AS title, i.report AS report, i.routes AS routes, i.stretch AS stretch, i.closedEids AS closedEids, i.status AS status, i.openedMs AS openedMs ORDER BY i.openedMs DESC LIMIT 1fork the sandboxon detour:fork:<visitor>:<fork>
GRAPH.COPY detour:sbx:<visitor> detour:fork:<visitor>:<fork>drop closed segments in the forkon detour:fork:<visitor>:<fork>
MATCH ()-[r:RUNS {closed: true}]->() DELETE rdrop closed rides in the forkon detour:fork:<visitor>:<fork>
MATCH ()-[r:RIDE {closed: true}]->() DELETE rdisplaced riderson detour:demand
MATCH (o:Station)-[d:DEMAND]->(x:Station) WHERE any(e IN d.via WHERE e IN $closed) RETURN o.id AS o, x.id AS d, d.riders AS riders, d.via AS viafree displaced riders' capacityon detour:fork:<visitor>:<fork>
UNWIND $freed AS f MATCH ()-[r:RUNS {eid: f.eid}]->() SET r.freed = f.riders, r.spare = CASE WHEN r.capacity - r.load + f.riders > 0 THEN r.capacity - r.load + f.riders ELSE 0 ENDforward source and sinkon detour:fork:<visitor>:<fork>
CREATE (src:Hub {id: $src}), (snk:Hub {id: $snk}) WITH src, snk UNWIND $sources AS s MATCH (x:Station {id: s.id}) CREATE (src)-[:FLOW {spare: s.cap, kind: $kind, ref: -1}]->(x)forward sinkson detour:fork:<visitor>:<fork>
MATCH (snk:Hub {id: $snk}) UNWIND $sinks AS s MATCH (x:Station {id: s.id}) CREATE (x)-[:FLOW {spare: s.cap, kind: $kind, ref: -1}]->(snk)clear corridor flow edgeson detour:fork:<visitor>:<fork>
MATCH (:Station)-[f:FLOW]->(:Station) DELETE faggregate spare capacity by corridoron detour:fork:<visitor>:<fork>
MATCH (a:Station)-[r]->(b:Station) WHERE type(r) IN $kinds WITH a, b, sum(r.spare) AS spare, collect(CASE type(r) WHEN 'RUNS' THEN 'R' + toString(r.eid) WHEN 'WALK' THEN 'W' + toString(r.wid) ELSE 'B' + toString(r.bid) END) AS refs CREATE (a)-[:FLOW {spare: spare, kind: 'NET', refs: refs}]->(b)forward max flowon detour:fork:<visitor>:<fork>
MATCH (src:Hub {id: $src}), (snk:Hub {id: $snk}) CALL algo.maxFlow({sourceNodes: [src], targetNodes: [snk], relationshipTypes: ['FLOW'], capacityProperty: 'spare'}) YIELD maxFlow, edges, edgeFlows RETURN maxFlow, [e IN edges | [e.kind, startNode(e).id, endNode(e).id]] AS es, edgeFlows AS flowsforward residual graphon detour:fork:<visitor>:<fork>
MATCH (a)-[r:FLOW]->(b) WHERE r.kind IN $kinds RETURN r.kind AS kind, a.id AS a, b.id AS b, r.spare AS cap, r.refs AS refsreroute displaced riders on fastest open pathson detour:fork:<visitor>:<fork>
UNWIND $pairs AS p MATCH (a:Station {id: p.o}), (b:Station {id: p.d}) CALL algo.SPpaths({sourceNode: a, targetNode: b, relTypes: $types, weightProp: 'minutes'}) YIELD path, pathWeight RETURN p.o AS o, p.d AS d, pathWeight AS minutes, [r IN relationships(path) WHERE type(r) = 'RIDE' | r.eid] AS eids, [r IN relationships(path) WHERE type(r) = 'BUS' | [r.bid, startNode(r).id]] AS bids, [r IN relationships(path) WHERE type(r) = 'WALK' | r.wid] AS walksfork for this planon detour:fork:<visitor>:<fork>
GRAPH.COPY detour:fork:<visitor>:<fork> detour:fork:<visitor>:<fork>add shuttle legson detour:fork:<visitor>:<fork>
UNWIND $legs AS l MATCH (a:Station {id: l.from}), (b:Station {id: l.to}) CREATE (a)-[:BUS {bid: l.bid, capacity: l.capacity, spare: l.capacity, minutes: l.minutes, busesPerHour: l.busesPerHour}]->(b), (b)-[:BUS {bid: l.bid, capacity: l.capacity, spare: l.capacity, minutes: l.minutes, busesPerHour: l.busesPerHour}]->(a)bus legson detour:fork:<visitor>:<fork>
MATCH (a)-[r:BUS]->(b) RETURN r.bid AS bid, a.id AS a, b.id AS b, r.capacity AS caprecord the planon detour:sbx:<visitor>
MATCH (i:Incident {id: $incident}), (ag:Agent {id: $agent}) CREATE (ag)-[:PROPOSED {atMs: timestamp()}]->(p:Plan {id: $id, label: $label, status: 'simulated', legs: $legsJson, vehicles: $vehicles, overflowBefore: $ob, overflowAfter: $oa, unmetBefore: $ub, unmetAfter: $ua, strandedBefore: $sb, strandedAfter: $sa, crowding: $crowding, createdMs: timestamp()})-[:FOR]->(i)
Publish the planpublish_plan8
open incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) RETURN i.id AS id, i.title AS title, i.report AS report, i.routes AS routes, i.stretch AS stretch, i.closedEids AS closedEids, i.status AS status, i.openedMs AS openedMs ORDER BY i.openedMs DESC LIMIT 1plans for the incidenton detour:sbx:<visitor>
MATCH (p:Plan)-[:FOR]->(i:Incident) WHERE $incident = '' OR i.id = $incident RETURN p.id AS id, p.label AS label, p.status AS status, p.legs AS legs, p.vehicles AS vehicles, p.overflowBefore AS overflowBefore, p.overflowAfter AS overflowAfter, p.unmetBefore AS unmetBefore, p.unmetAfter AS unmetAfter, p.strandedBefore AS strandedBefore, p.strandedAfter AS strandedAfter, p.createdMs AS createdMs, p.publishedMs AS publishedMs, i.id AS incident ORDER BY createdMs DESC LIMIT 12remove earlier shuttleson detour:sbx:<visitor>
MATCH ()-[b:BUS]->() DELETE badd the plan's shuttle legson detour:sbx:<visitor>
UNWIND $legs AS l MATCH (a:Station {id: l.from}), (b:Station {id: l.to}) CREATE (a)-[:BUS {bid: l.bid, plan: $plan, capacity: l.capacity, spare: l.capacity, minutes: l.minutes, cost: l.minutes, busesPerHour: l.busesPerHour}]->(b), (b)-[:BUS {bid: l.bid, plan: $plan, capacity: l.capacity, spare: l.capacity, minutes: l.minutes, cost: l.minutes, busesPerHour: l.busesPerHour}]->(a)what overflows with the plan runningon detour:sbx:<visitor>
MATCH (p:Plan {id: $plan}) RETURN p.crowding AS crowdingrouting cost is travel timeon detour:sbx:<visitor>
MATCH ()-[r:BOARD|ALIGHT|RIDE|WALK|BUS]->() SET r.cost = r.minutes, r.crowd = nullcrowded rides cost moreon detour:sbx:<visitor>
UNWIND $segs AS c MATCH ()-[r:RIDE {eid: c.eid}]->() SET r.crowd = c.pct, r.cost = r.minutes * c.factor RETURN count(r) AS npublish and hand off to the rider deskon detour:sbx:<visitor>
MATCH (p:Plan {id: $plan}), (c:Agent {id: 'controller'}), (r:Agent {id: 'rider-desk'}) SET p.status = 'published', p.publishedMs = timestamp(), p.approvedBy = $approvedBy CREATE (c)-[:HANDED_OFF {atMs: timestamp(), incident: $incident, plan: $plan, note: 'Plan published. Tell every remembered rider whose trip crosses the closure what to take instead.'}]->(r)
Steer riders, no busespropose_steering3
open incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) RETURN i.id AS id, i.title AS title, i.report AS report, i.routes AS routes, i.stretch AS stretch, i.closedEids AS closedEids, i.status AS status, i.openedMs AS openedMs ORDER BY i.openedMs DESC LIMIT 1the measured capacity pictureon detour:sbx:<visitor>
MATCH (i:Incident {id: $id}) RETURN i.overflow AS overflow, i.stranded AS stranded, i.unmet AS unmetrecord the planon detour:sbx:<visitor>
MATCH (i:Incident {id: $incident}), (ag:Agent {id: $agent}) CREATE (ag)-[:PROPOSED {atMs: timestamp()}]->(p:Plan {id: $id, label: $label, status: 'simulated', legs: '[]', vehicles: 0, overflowBefore: $o, overflowAfter: $o, unmetBefore: $u, unmetAfter: $u, strandedBefore: $s, strandedAfter: $s, createdMs: timestamp()})-[:FOR]->(i)
Remembered riders hitriders_affected2
open incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) RETURN i.id AS id, i.title AS title, i.report AS report, i.routes AS routes, i.stretch AS stretch, i.closedEids AS closedEids, i.status AS status, i.openedMs AS openedMs ORDER BY i.openedMs DESC LIMIT 1riders and what they prefer nowon detour:sbx:<visitor>
MATCH (r:Rider)-[:STARTS_AT]->(a:Station), (r)-[:ENDS_AT]->(b:Station) OPTIONAL MATCH (r)-[p:PREFERS]->(pref:Preference) WHERE p.validToMs IS NULL WITH r, a, b, collect(CASE WHEN pref IS NULL THEN null ELSE {key: pref.key, value: pref.value, sinceMs: p.validFromMs} END) AS prefs RETURN r.id AS id, r.name AS name, r.persona AS persona, r.departs AS departs, r.stepFree AS stepFree, a.id AS from, b.id AS to, r.usualEids AS usualEids, r.usualStations AS usualStations, r.usualMinutes AS usualMinutes, prefs AS preferences ORDER BY name
Route a riderroute_rider5
riders and what they prefer nowon detour:sbx:<visitor>
MATCH (r:Rider)-[:STARTS_AT]->(a:Station), (r)-[:ENDS_AT]->(b:Station) OPTIONAL MATCH (r)-[p:PREFERS]->(pref:Preference) WHERE p.validToMs IS NULL WITH r, a, b, collect(CASE WHEN pref IS NULL THEN null ELSE {key: pref.key, value: pref.value, sinceMs: p.validFromMs} END) AS prefs RETURN r.id AS id, r.name AS name, r.persona AS persona, r.departs AS departs, r.stepFree AS stepFree, a.id AS from, b.id AS to, r.usualEids AS usualEids, r.usualStations AS usualStations, r.usualMinutes AS usualMinutes, prefs AS preferences ORDER BY namecurrent accessibility outages (memory)on detour:sbx:<visitor>
MATCH (f:Finding {kind: 'accessibility'})-[:ABOUT]->(s:Station) WHERE f.validToMs IS NULL OPTIONAL MATCH (f)-[:ABOUT]->(r:Route) RETURN s.id AS id, f.text AS text, f.validFromMs AS sinceMs, collect(r.id) AS routesrides over capacity right nowon detour:sbx:<visitor>
MATCH ()-[r:RIDE]->() WHERE r.crowd > 100 RETURN count(r) AS npaths weighed by minutes and crowdingon detour:sbx:<visitor>
MATCH (a:Station {id: $from}), (b:Station {id: $to}) CALL algo.SPpaths({sourceNode: a, targetNode: b, relTypes: ['BOARD', 'RIDE', 'ALIGHT', 'WALK', 'BUS'], weightProp: 'cost', pathCount: $k}) YIELD path, pathWeight RETURN [n IN nodes(path) | {station: coalesce(n.station, n.id), route: n.route, platform: hasLabels(n, ['Platform'])}] AS nodes, [r IN relationships(path) | {t: type(r), eid: r.eid, minutes: r.minutes, crowd: r.crowd}] AS rels, pathWeight AS weight ORDER BY weightfastest paths, ignoring crowdingon detour:sbx:<visitor>
MATCH (a:Station {id: $from}), (b:Station {id: $to}) CALL algo.SPpaths({sourceNode: a, targetNode: b, relTypes: ['BOARD', 'RIDE', 'ALIGHT', 'WALK', 'BUS'], weightProp: 'minutes', pathCount: $k}) YIELD path, pathWeight RETURN [n IN nodes(path) | {station: coalesce(n.station, n.id), route: n.route, platform: hasLabels(n, ['Platform'])}] AS nodes, [r IN relationships(path) | {t: type(r), eid: r.eid, minutes: r.minutes, crowd: r.crowd}] AS rels, pathWeight AS weight ORDER BY weight
Remember a findingremember3
open incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) RETURN i.id AS id, i.title AS title, i.report AS report, i.routes AS routes, i.stretch AS stretch, i.closedEids AS closedEids, i.status AS status, i.openedMs AS openedMs ORDER BY i.openedMs DESC LIMIT 1write findingon detour:sbx:<visitor>
MATCH (ag:Agent {id: $agent}) WITH ag, [] AS current CREATE (ag)-[:WROTE]->(f:Finding {id: $id, text: $text, kind: $kind, confidence: $confidence, validFromMs: timestamp(), learnedMs: timestamp()}) FOREACH (c IN current | SET c.validToMs = timestamp() CREATE (f)-[:SUPERSEDES]->(c)) WITH f, [c IN current | c.id] AS superseded OPTIONAL MATCH (n) WHERE (n:Station AND n.id IN $stations) OR (n:Route AND n.id IN $routes) WITH f, superseded, collect(n) AS about FOREACH (x IN about | CREATE (f)-[:ABOUT]->(x)) WITH f, superseded OPTIONAL MATCH (i:Incident {id: $incident}) FOREACH (_ IN CASE WHEN i IS NULL THEN [] ELSE [1] END | CREATE (f)-[:FROM]->(i)) RETURN f.id AS id, supersededwrite finding and close the one it supersedeson detour:sbx:<visitor>
MATCH (ag:Agent {id: $agent}) OPTIONAL MATCH (old:Finding {id: $old}) WITH ag, old WHERE old IS NULL OR old.validToMs IS NULL WITH ag, CASE WHEN old IS NULL THEN [] ELSE [old] END AS current CREATE (ag)-[:WROTE]->(f:Finding {id: $id, text: $text, kind: $kind, confidence: $confidence, validFromMs: timestamp(), learnedMs: timestamp()}) FOREACH (c IN current | SET c.validToMs = timestamp() CREATE (f)-[:SUPERSEDES]->(c)) WITH f, [c IN current | c.id] AS superseded OPTIONAL MATCH (n) WHERE (n:Station AND n.id IN $stations) OR (n:Route AND n.id IN $routes) WITH f, superseded, collect(n) AS about FOREACH (x IN about | CREATE (f)-[:ABOUT]->(x)) WITH f, superseded OPTIONAL MATCH (i:Incident {id: $incident}) FOREACH (_ IN CASE WHEN i IS NULL THEN [] ELSE [1] END | CREATE (f)-[:FROM]->(i)) RETURN f.id AS id, superseded
Resolve the incidentresolve_incident12
open incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) RETURN i.id AS id, i.title AS title, i.report AS report, i.routes AS routes, i.stretch AS stretch, i.closedEids AS closedEids, i.status AS status, i.openedMs AS openedMs ORDER BY i.openedMs DESC LIMIT 1the published planon detour:sbx:<visitor>
MATCH (p:Plan {status: 'published'})-[:FOR]->(:Incident {id: $incident}) RETURN p.id AS id, p.legs AS legs, p.publishedMs AS publishedMs ORDER BY publishedMs DESC LIMIT 1remove shuttleson detour:sbx:<visitor>
MATCH ()-[b:BUS]->() DELETE breopen closed segmentson detour:sbx:<visitor>
MATCH ()-[r:RUNS {closed: true}]->() SET r.closed = false, r.minutes = r.baseMinutes, r.spare = CASE WHEN r.capacity > r.load THEN r.capacity - r.load ELSE 0 END RETURN count(r) AS nreopen platform rideson detour:sbx:<visitor>
MATCH ()-[r:RIDE {closed: true}]->() SET r.closed = false, r.minutes = r.baseMinutesclear crowding costson detour:sbx:<visitor>
MATCH ()-[r:BOARD|ALIGHT|RIDE|WALK|BUS]->() WHERE r.cost IS NOT NULL SET r.cost = null, r.crowd = nullclose out incidenton detour:sbx:<visitor>
MATCH (i:Incident {status: 'open'}) SET i.status = $status, i.closedMs = timestamp()playbooks for these routeson detour:sbx:<visitor>
MATCH (p:Playbook)-[:FOR]->(r:Route) WHERE r.id IN $routes WITH p, collect(r.id) AS routes RETURN p.id AS id, p.title AS title, p.steps AS steps, p.stations AS stations, coalesce(p.revision, 1) AS revision, routescount the playbooks this incident appliedon detour:sbx:<visitor>
MATCH (p:Playbook), (i:Incident {id: $incident}) WHERE p.id IN $ids SET p.uses = p.uses + 1, p.lastUsedMs = timestamp() CREATE (p)-[:APPLIED {atMs: timestamp()}]->(i)findings about these stations and routeson detour:sbx:<visitor>
MATCH (f:Finding)-[:ABOUT]->(n) WHERE (n:Station AND n.id IN $stations) OR (n:Route AND n.id IN $routes) WITH DISTINCT f OPTIONAL MATCH (f)-[:FROM]->(i:Incident) OPTIONAL MATCH (ag:Agent)-[:WROTE]->(f) RETURN f.id AS id, f.text AS text, f.kind AS kind, f.confidence AS confidence, f.validFromMs AS validFromMs, f.validToMs AS validToMs, f.example AS example, i.id AS incident, ag.name AS agent ORDER BY validFromMs DESC LIMIT 12past incidents on these routeson detour:sbx:<visitor>
MATCH (i:Incident) WHERE i.status <> 'open' AND any(r IN i.routes WHERE r IN $routes) OPTIONAL MATCH (s:Shuttle)-[:FOR]->(i) OPTIONAL MATCH (s)-[:FROM]->(a:Station) OPTIONAL MATCH (s)-[:TO]->(b:Station) WITH i, collect(CASE WHEN s IS NULL THEN null ELSE {from: a.name, to: b.name, bph: s.busesPerHour, planned: s.planned, observed: s.observed} END) AS shuttles RETURN i.id AS id, i.title AS title, i.openedMs AS openedMs, i.example AS example, shuttles ORDER BY openedMs DESC LIMIT 5playbooks for these routeson detour:sbx:<visitor>
MATCH (p:Playbook)-[:FOR]->(r:Route) WHERE r.id IN $routes RETURN DISTINCT p.id AS id, p.title AS title, p.steps AS steps, p.uses AS uses, p.lastUsedMs AS lastUsedMs