viafrei 1.3.15 → 1.3.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,455 @@
3
3
  All notable changes to this package are documented here. The format follows Keep
4
4
  a Changelog and the versions follow Semantic Versioning.
5
5
 
6
+ ## [Unreleased]
7
+
8
+ Nothing yet.
9
+
10
+ ## [1.3.22] - 2026-09-28
11
+
12
+ **A fresh snapshot of the production server, and the version number that goes with it.
13
+ Nothing else, and the diff is the evidence: outside this entry the release is nine
14
+ insertions and nine deletions, every one of them a version string or a date** — four in
15
+ `API.md`, two in the snapshot, one in the manifest and two in the lockfile.
16
+
17
+ The service moved from 1.3.16 to 1.3.22 in six patch releases while this package sat at
18
+ 1.3.16 — one of which carried no runtime code and was never deployed, so five
19
+ deployments. **None of them changed the LISTED surface**: the tools, resources, templates
20
+ and prompts a client is offered, which is what this package documents and all a list
21
+ method can show. It is not a claim about what a read returns, and one of the five did
22
+ change that — 1.3.19 corrected an attribution resource's contents. That is why nothing in
23
+ `API.md` moves except its header. Measured rather than assumed: the live server was asked to
24
+ describe itself again and the answer was compared, field by field, against the snapshot
25
+ shipped in 1.3.16 — every tool name, title, description and annotation, every input
26
+ schema including each parameter's type, bounds and pattern, all ten resources, both
27
+ resource templates, all nine prompts, the server's own instructions, and the advertised
28
+ capabilities. **Zero differences.** The five deployed releases were server-side data and
29
+ rendering fixes: a Bundesland decided by the order map extracts had been loaded in, an
30
+ attribution resource that claimed we hold nothing from a source we serve, a batch of
31
+ defects closing the 1.3 milestone, and a place answer for "Munich" restored. The sixth,
32
+ 1.3.17, says in its own block that it carries no runtime code and is not deployed.
33
+
34
+ So this release is worth exactly one thing to a reader, and it is worth being plain about
35
+ which: the document in the package now says it was captured on 2026-09-28 from server
36
+ 1.3.22, rather than on 2026-09-27 from 1.3.16 — one day and six version numbers. A dated
37
+ snapshot whose version is behind invites the reader to wonder what has changed since, and
38
+ here the answer is nothing, which they can only know if the snapshot says so.
39
+
40
+ **No behaviour changes.** `dist/` is built rather than committed and no file under `src/`
41
+ has changed since the 1.3.16 tag, which is the evidence for the claim. No flag, default
42
+ or exit code moves. Two of the seven `files` entries change — `API.md` and
43
+ `CHANGELOG.md` — and the tarball still holds 21 paths.
44
+
45
+ ### Changed
46
+
47
+ - **`catalogue.json` re-captured** from the production server on 2026-09-28 over protocol
48
+ `2025-06-18`: `initialize` plus `tools/list`, `resources/list`,
49
+ `resources/templates/list` and `prompts/list`. **No tool was invoked**, so no upstream
50
+ provider was contacted — listing is metadata, calling is traffic, and one provider
51
+ behind this service puts the access itself at risk if queried needlessly. The same two
52
+ substitutions its own `$comment` documents were applied again and asserted rather than
53
+ trusted: 18 per-tool `$schema` declarations dropped, and the two 288-character
54
+ ISO-8601 date patterns stored as their length, at `get_train_departures.when` and
55
+ `watch_situation.until`. Both counts were asserted by the one-off script that made the
56
+ capture, which refused to write the file unless they came out exactly so — **not** by
57
+ anything this repository carries. An earlier draft of this sentence said "the capture is
58
+ refused", present tense, which reads as a standing property of the tooling and would
59
+ send a reader looking for a gate that is not there. There is no capture script here, and
60
+ re-capturing is a deliberate act performed by hand.
61
+
62
+ - **`API.md` regenerated**, which under `npm run check:docs` it has to be. The only lines
63
+ that differ are the capture date in the header paragraph, the server version in the
64
+ table, the captured-from line, and the footer — because the server said the same thing
65
+ it said the day before.
66
+
67
+ ### A note on version matching, because 1.3.16 made a promise this release cannot keep
68
+
69
+ 1.3.16 argued its number should match the running service so that "somebody comparing the
70
+ two now reads one number instead of wondering which is behind". That argument does not
71
+ survive contact with how fast the service ships: production went 1.3.16 → 1.3.18 → 1.3.22
72
+ in under two hours, twice while this release was being measured. A package cannot track
73
+ that, and pretending otherwise means every publish is stale on arrival.
74
+
75
+ What this package can honestly say is what it now says: the snapshot carries the version
76
+ and date of the server it was taken from, and the document names the running server as the
77
+ source of truth for anything newer. The numbers agree today because this release chose
78
+ once more to make them agree — it stepped over 1.3.17 to 1.3.21 to land on production's
79
+ number, which a reader comparing this version with the previous one in the registry can
80
+ see for themselves. Calling that a coincidence would be the same overclaim in a smaller
81
+ font. It is the last time alignment is a reason to cut a release: from here the snapshot's
82
+ own version and date carry that information, and no release should be cut for the sole
83
+ purpose of making the numbers agree.
84
+
85
+ ## [1.3.16] - 2026-09-27
86
+
87
+ **This is the release that delivers the licence corrections to the people the
88
+ licences point at.** `NOTICE` is the file Apache-2.0 § 4(d) makes every downstream
89
+ redistributor carry, and until this publish the corrected text existed only in the
90
+ repository: the 1.3.15 tarball on the registry still carries the previous wording. So
91
+ publishing is not a side effect of the correction, it **is** the correction — which
92
+ is the whole reason this release exists, because nothing in `dist/` changes.
93
+
94
+ The number matches the running service rather than counting the changes here.
95
+ Production answered `initialize` with `1.3.16` when it was asked on 2026-09-27, and
96
+ this package is the bridge to that server, so somebody comparing the two now reads
97
+ one number instead of wondering which is behind.
98
+
99
+ **No behaviour changes.** `dist/` is built rather than committed, and no file under
100
+ `src/` has changed since the 1.3.15 tag — which is the evidence for the claim, since
101
+ a diff of an untracked directory would be no evidence at all. So no flag, default or
102
+ exit code moves. **Six** of the seven `files` entries do change —
103
+ `README.md`, `LICENSE`, `NOTICE`, `SOURCES.md`, `CHANGELOG.md` and the new
104
+ `API.md` — and the tarball goes from twenty paths to twenty-one. (Six *entries*,
105
+ not six files: `dist` is a directory.)
106
+
107
+ This paragraph has now been wrong twice, which is worth leaving on the record
108
+ rather than tidying away. The first draft called these "the repository's own
109
+ development scripts" and said the next tarball would differ in two files — true of
110
+ the first change here, false once the licence work landed beside it. The second
111
+ draft said **four** files and omitted `LICENSE` — and the commit that wrote that
112
+ sentence is the same commit that changed `LICENSE`, so it was false at the moment
113
+ it was written, in the paragraph written to correct a false count. The count moved
114
+ once more at the cut, because `API.md` joined `files` in this release. Every
115
+ version of it has been computed from `package.json`'s `files` intersected with
116
+ `git diff --name-only v1.3.15..HEAD`, with the path count from `npm pack --json`,
117
+ rather than counted by eye — which is the only way this sentence has ever been
118
+ right.
119
+
120
+ ### Added
121
+
122
+ - **A sweep that keeps the twenty-first call site from being written by accident**
123
+ (`scripts/tools.test.mjs`, `npm run test:tools`, and a step in CI). Eighteen
124
+ cases: what `resolveTool` accepts and refuses, that its directory list is
125
+ root-owned and not group- or other-writable (the property the module relies on,
126
+ rather than the list that is supposed to have it), that its contents cannot be
127
+ extended at runtime, that an `npm_execpath` which is absolute, real and readable
128
+ but not npm is refused, and then a sweep of all thirty-two source files,
129
+ walked recursively, for a spawn whose program is a bare quoted name.
130
+
131
+ It carries three preconditions, because a gate whose input is absent reports
132
+ success about what it never read: **every** source root must have contributed a
133
+ file and an unreadable root is a failure rather than an absence; the expression
134
+ must locate a program argument in a real file of this repository, proved by
135
+ putting a real call's program back to a literal in memory and requiring it to be
136
+ found; and it must go red on a planted bad call, with the fixtures assembled at
137
+ runtime so no literal in this file can satisfy its own sweep.
138
+
139
+ Both of the last two exist because the first drafts did not do what their own
140
+ comments claimed. The floor was a floor on the *sum*, and `scripts` and `test`
141
+ clear it between them — so `src/` could be deleted entirely and the run still
142
+ printed ok at 21 files. And the "it can see a call" precondition evaluated the
143
+ real expression only on the negative case; the positive half used a different,
144
+ simpler pattern and never asked the real one to read a file.
145
+
146
+ Proved the way a gate has to be: one real call site reverted to a bare name
147
+ turns the sweep red, one source root removed turns precondition 1 red, and the
148
+ files were restored byte-identical afterwards.
149
+
150
+ - **[API.md](API.md): a reference for all 18 tools, generated rather than written.**
151
+ `catalogue.json` is what the production server answered when it was asked to
152
+ describe itself — `initialize` plus `tools/list`, `resources/list`,
153
+ `resources/templates/list` and `prompts/list`, captured on 2026-09-27 from
154
+ server `1.3.16`. `scripts/gen-api-doc.mjs` renders the document from it
155
+ (`npm run docs:api`), and `npm run check:docs` in CI fails if the two have
156
+ drifted. Every tool description in it is the server's own text, verbatim,
157
+ because that text is what an assistant reads when it decides which tool to
158
+ call — paraphrasing it would document a different server.
159
+
160
+ **No tool was invoked to produce the snapshot.** Listing is metadata; calling is
161
+ traffic, and one of the providers behind this service sets a floor on how often a
162
+ station may be queried, with the access itself at risk if it is exceeded. The
163
+ capture is therefore five list methods and nothing else.
164
+
165
+ **What generating it does and does not fix**, because README.md already argued
166
+ the opposite case and that argument was right. Drift between the document and the
167
+ snapshot is now impossible to keep — CI regenerates and compares. Drift between
168
+ the snapshot and the live server is not fixed by anything, because a capture is a
169
+ point in time. So API.md's own header says it is a dated snapshot, carries the
170
+ date, and names the running server as the source of truth — in that file and not
171
+ only in README.md. Both of them ship in the tarball, so that is not the reason;
172
+ the reason is that a qualification has to travel with the document it qualifies,
173
+ because somebody who opens the catalogue to look up a parameter has no occasion
174
+ to read the page beside it.
175
+
176
+ Two substitutions in the snapshot, both recorded in its own `$comment` rather
177
+ than left as silent differences from what the server sent: the 18 per-tool
178
+ `$schema` declarations are dropped as one constant repeated 18 times, and two
179
+ 288-character date patterns are stored as their LENGTH. The second was forced by
180
+ the leak sweep and the interesting part is which side gave way — such a regex
181
+ spells its arithmetic as character classes of selected digits, which a digit-run
182
+ scanner cannot tell from a five-digit internal value. Both remedies on the
183
+ sweep's side would have been falsehoods: narrowing the scanner weakens it for
184
+ every file, and an allow-list entry would record that a character class is a
185
+ number this project publishes. So the text that cannot be scanned is not stored.
186
+ Nothing is hidden by it — the document already summarised a pattern over 60
187
+ characters by its length, and the server hands anyone the full expression.
188
+
189
+ One parameter is an object whose seven keys each carry their own bounds, and the
190
+ first draft rendered it as a dash in both the default and the constraints
191
+ column — the table's strongest claim broken on the row with the most to say. The
192
+ cause defeated the safety net as well: `properties` sat in the set of keywords
193
+ the renderer treats as handled, so the fallback that lists an unrecognised
194
+ keyword as a bare name never fired. The keyword was known; it was simply never
195
+ rendered.
196
+
197
+ - **A self-test for the API-reference generator** (`scripts/gen-api-doc.test.mjs`,
198
+ `npm run test:docs`, and a step in CI). Its cases include the drift check refusing a
199
+ hand-edited file, never writing during `--check`, and naming `docs:api` when the
200
+ document is absent; eight snapshot mutations each refused with a named reason; a
201
+ malformed `required` rendering rather than throwing; a planted nested `default`
202
+ reaching the document; a union type in a table cell and in a bullet; an
203
+ array-of-scalar; and the reported line count agreeing with `wc -l`. The run prints
204
+ the total, which is the number to trust — this file has been wrong about counts
205
+ more than once, so it states none here.
206
+
207
+ It exists because the generator was the only script here making claims with nothing
208
+ checking them, and the same six mutants were being re-run by hand across three
209
+ review rounds — a check performed by remembering is not a check. It also earned
210
+ itself immediately, by catching two defects nothing else could:
211
+
212
+ The whitespace-flattening expression, rewritten to remove a super-linear
213
+ backtracking pattern, **was not equivalent to what it replaced.** It handled only
214
+ spaces and tabs beside the newline, so a `\r\n` line ending would have left a stray
215
+ carriage return in the document. API.md regenerated byte-identical, `--check`
216
+ passed, every gate was green — because this snapshot contains no CRLF. An output
217
+ comparison can only speak about the input it was given, so the test compares the two
218
+ **expressions** over every string up to length four drawn from a whitespace-heavy
219
+ alphabet, plus random longer ones, and asserts first that the alphabet can expose
220
+ the bug that shipped.
221
+
222
+ And a **union type was escaped twice**: `typeOf` joined with an already-escaped pipe
223
+ and the cell renderer escaped that pipe again, giving `string \\| null` — a literal
224
+ backslash, and a bare pipe left to end the table row early. In a bullet, which is not
225
+ a table, it produced a stray backslash instead. No tool in this snapshot declares a
226
+ union type, so neither was reachable and no comparison of documents could have found
227
+ it. Escaping now happens in one place, the one that knows it is writing a table cell.
228
+
229
+ - **[SUPPORT.md](SUPPORT.md)**, so GitHub's issue chooser has somewhere to point:
230
+ where a question, a wrong answer and a security report each go, and what makes a
231
+ report actionable.
232
+
233
+ - **README gains a Documentation table and five worked use cases** — a motorway
234
+ briefing, a broken commute including a station lift that is out, an EV weekend, a
235
+ dispatcher's morning brief, and a local-guide agent — each named by the question
236
+ it answers rather than by the tools it calls.
237
+
238
+ ### Changed
239
+
240
+ - **One entry of 13 characters left the leak sweep's private-name list, under that
241
+ list's own rule 1.** The rule is stated in `scripts/rules.json`: an entry must not
242
+ be a substring of text this repository legitimately prints, because such an entry
243
+ can never be satisfied, and the only ways out are deleting it or narrowing the
244
+ scanner — which is strictly worse. There is no mechanical test for it; the sweep
245
+ going red *is* the test, and it went red. Declared narrowing: one spelling of 13
246
+ characters is no longer matched anywhere. The list's second rule is not engaged,
247
+ which decides that nothing else follows: the audit recorded in that file says
248
+ nothing on the list is from the class whose harm is confirming a guess —
249
+ credentials, tokens, session or contract identifiers, access-granting hostnames,
250
+ personal data — so there is no rotation and no rename here.
251
+
252
+ Recorded that way on purpose, and it is a change of practice rather than of
253
+ style. The two removals recorded under 0.0.9 described what the entries were
254
+ ABOUT, and taken together those descriptions narrowed the candidate space for a
255
+ live entry further than a hash does — the same caption failure the number
256
+ allow-list refuses on the facing page. The file now ends with the ruling: record
257
+ the rule, the lengths and the narrowing, never the subject. This is the first
258
+ entry written under it. The older wordings stay where they are, because editing
259
+ a published file does not unpublish it.
260
+
261
+ Coverage was asked of the change rather than asserted, since going from six
262
+ entries to five must not leave the sweep reporting PASS about what it no longer
263
+ looks for. The matcher was exercised per slot without needing any real name:
264
+ the list replaced by five planted names, each planted in text, five reds
265
+ required, and a negative control that stays clean. The sweep also prints the
266
+ list's size on every run, so the change is visible rather than silent, and the
267
+ gate self-test still refuses an emptied list. The window `minTokenLength` and
268
+ `maxTokenLength` is unchanged at 7 and 15: both are stored literals, neither is
269
+ derived from the list, and the removed entry sat at neither bound, so no
270
+ published number narrowed.
271
+
272
+ - **`numbers.allowed` admits four values the SERVER publishes about itself** — two
273
+ inside tool descriptions and two as bounds in its own input schemas — under one
274
+ general rule written into that file rather than a caption each, because a caption
275
+ per value is the construction the list exists to avoid. It is safe to state in
276
+ general terms because the sweep already refuses an allowed number that occurs
277
+ nowhere in the tree, so an exemption cannot outlive its reason.
278
+
279
+ - **Four statements about other people's licences were wrong, and they are the kind
280
+ a reader acts on.** Every one was verified against its own source before it was
281
+ changed, and each correction says that an earlier version had it wrong rather than
282
+ quietly reading better.
283
+ - **Share-alike reaches adaptations, not aggregations.** `SOURCES.md`, `NOTICE`
284
+ and `README.md` all said DELFI data must not be blended into a result under a
285
+ different licence. CC BY-SA 4.0 art. 3(b) is expressed only over Adapted
286
+ Material (art. 1(a)); showing licensed data beside another source's data is an
287
+ aggregation, and Creative Commons states in terms that the condition "applies
288
+ only for works considered adaptations under copyright law, not simply in
289
+ collections with other works". Worse than generous in two ways: art. 3(b)(3)
290
+ forbids imposing terms that restrict rights the licence grants, which is what
291
+ telling you an aggregation is forbidden does — and the rule as written condemned
292
+ this service, since a weather answer can name `["dwd","osm"]`.
293
+ - **The DELFI licence version was sourced to nothing.** Measured at both levels
294
+ on 2026-09-27: the GovData record for the realtime feed carries the unversioned
295
+ licence URI and an empty dataset-level licence, and the national access point's
296
+ own metadata reports the same unversioned term. The publisher's sibling feeds in
297
+ the same catalogue *are* versioned, so it states a version when it means one.
298
+ The page now names the family, shows the evidence, and says to treat the
299
+ share-alike as applying regardless — because the version decides what a
300
+ recipient may put on a derivative (art. 3(b)(1)), and 3.0 unported has no
301
+ express database-rights clause where 4.0 art. 4 does.
302
+ - **§ 7 DWD-Gesetz prescribes a duty, not a wording.** Fetched verbatim: the
303
+ section is headed *Quellenschutz*, requires distribution to be "nur unter
304
+ Angabe der Quelle zulässig" and sets no text — and its second sentence adds
305
+ that fuller protection under the Urheberrechtsgesetz "bleibt davon unberührt",
306
+ so attribution is the statute's minimum rather than the whole of what may be
307
+ owed. The three words come from the DWD's own guidance, which also permits the
308
+ logo form — so a reader told the statute fixes the characters might refuse a
309
+ form the publisher expressly allows.
310
+ - **`NOTICE` understated the ODbL duty, and it is the file that republishes
311
+ itself** (Apache-2.0 § 4(d) makes every downstream redistributor carry it). It
312
+ said "address results" where the predicate is which *table* answered — place
313
+ resolution falls through the gazetteer to the OSM tables, so any answer about a
314
+ place may be OSM-derived — and offered one extract where the § 4.6 offer covers
315
+ both addresses and points of interest.
316
+
317
+ ### Fixed
318
+
319
+ - **Every external program these scripts run is now resolved to an absolute path
320
+ instead of being looked up on `$PATH`** (`scripts/tools.mjs`). `git`, `tar`,
321
+ `npm` and a `mkdir` were spawned by bare name, which means the environment — not
322
+ this repository — decided which program actually ran. That matters more here
323
+ than it would elsewhere: one of these scripts is the leak sweep that decides
324
+ whether a commit may be published, and another is the hygiene gate that reads
325
+ the tarball about to be uploaded to the registry. A gate whose implementation
326
+ the caller can substitute is not a gate. SonarCloud reported seven of the call
327
+ sites as `javascript:S4036` and the project's Security Rating on new code stood
328
+ at B because of them.
329
+ - `git` and `tar` now come from `/usr/bin` or `/bin` only. `/usr/local/bin` and
330
+ `/opt/homebrew/bin` are deliberately not searched: they are writable by the
331
+ logged-in user on a normal developer machine, so admitting them would
332
+ reinstate the substitution this change removes. A tool that is genuinely
333
+ elsewhere makes the scripts refuse and say where they looked, which is a
334
+ better failure than quietly running something else.
335
+ - `npm` is no longer treated as a program at all. It is a JavaScript file, so it
336
+ is run as `<absolute node> <absolute npm-cli.js>`; node's own path is
337
+ `process.execPath`, which nothing can substitute. `mkdir` was replaced by
338
+ `fs.mkdirSync` — no subprocess at all.
339
+ - **Twenty call sites changed, not the seven that were reported.** The other
340
+ thirteen are in the two self-tests, which SonarCloud does not analyse. Leaving
341
+ them would have left the rule true of the code and false of the repository,
342
+ and a rule with a quiet exemption is the one nobody remembers when adding the
343
+ next call. (Counted per file in the finished tree: `check-leaks.mjs` 2,
344
+ `check-tarball.mjs` 4, `npm-pack-json.mjs` 1 — Sonar's seven — then
345
+ `check-leaks.test.mjs` 3 and `check-tarball.test.mjs` 10. The first version of
346
+ this entry said nineteen, because it was counted with a single-line grep that
347
+ cannot see the one call whose program argument sits on its own line. A count
348
+ taken with the wrong instrument, in a release whose own subject is exactly
349
+ that.)
350
+
351
+ - **And the repair for that duplicated it, which the quality gate caught before
352
+ the merge.** The precondition was *copied* from one self-test into the other —
353
+ eleven lines — and SonarCloud failed the pull request on **3.1% duplication on
354
+ new code** against a 3% limit, over exactly that block. Copying was the wrong
355
+ half of the right idea: the answer was always one implementation used twice.
356
+ It now lives in `scripts/fixture-root.mjs` as `missingFixtureImports()`, and each
357
+ self-test keeps its own refusal wording, because the two name different builders
358
+ and exit by different routes — a difference that is real rather than incidental.
359
+ Re-proved in each: dropping `tools.mjs` from a caller's file list makes that file
360
+ refuse by name, and each names its own builder.
361
+
362
+ **Concentrating the guarantee doubled its blast radius, so it got the assertion
363
+ it never had.** One function now stands behind every self-test that uses it, so a
364
+ silent `return []` disarms all of them at once and restores the wrong-reason pass
365
+ that started this thread — a sweep that cannot start, reporting no findings. The
366
+ number of those callers is deliberately not written here or in the module: it grew
367
+ again inside this release, and the sentence that said "both" went stale unnoticed
368
+ in five places while the corrected wording lived in one. On an ordinary run they
369
+ only ever exercise the complete-fixture path, so
370
+ until now the "missing" branch was proved solely by hand-mutating a file list:
371
+ four times by two people, and never again by anything. Two cases cover both
372
+ directions on a temporary directory, and they are mutation-proved — a planted
373
+ `return []` reddens one, and ignoring the directory argument reddens both.
374
+
375
+ Worth recording as the shape rather than the incident. A missing precondition was
376
+ fixed by adding one; adding it introduced a duplicate of it; the gate caught the
377
+ duplicate. Three links, and every one of them was found by something other than
378
+ the test suite, which was green at each step. The file count in this entry moved
379
+ from twenty-eight to twenty-nine because of it, then to thirty when this release
380
+ added a source file of its own, and then to **thirty-two** as it added the generator's
381
+ self-test and the module they share. The first two were re-derived from `npm run test:tools` rather than
382
+ incremented by hand. The third was not, and the review round found it stale. The
383
+ fourth was not typed either: it was read out of `npm run test:tools`'s own output
384
+ and the edit refused to write a number the command did not report. That was a
385
+ one-off script in a scratchpad, not something this repository carries — said plainly
386
+ because an earlier draft of this very sentence claimed a committed artefact that
387
+ does not exist. This paragraph has been wrong before, and that was the first time
388
+ it invented an artefact rather than a number.
389
+
390
+ - **The leak sweep's own self-test had no precondition on the fixture it builds,
391
+ and reported a scope regression instead.** Both self-tests copy a named list of
392
+ files into a throwaway repository, and `tools.mjs` was missing from both lists.
393
+ `check-tarball.test.mjs` refused by name, which is what a precondition is for.
394
+ `check-leaks.test.mjs` had no such check: **measured**, three of its four cases
395
+ failed with messages about scope — "expected a clean exit, got 1", "the run does
396
+ not say which scope it used" — because every invocation died of
397
+ `ERR_MODULE_NOT_FOUND`, and the fourth case *passed*, since a sweep that cannot
398
+ start also cannot report a finding. That is worse than a red suite: it sends the
399
+ next reader after the ruleset instead of after a missing file. It now refuses by
400
+ name, proved by dropping the file again.
401
+
402
+ - **`npm_execpath` was trusted if it was merely absolute and ended in `.js`** —
403
+ which reversed this change's own thesis for npm. The decider had moved from
404
+ `$PATH` to an environment variable, and the local review demonstrated it by
405
+ pointing the variable at a hand-written file, which the tarball gate would then
406
+ have run as npm. The value must now also be *shaped* like npm's CLI, ending in
407
+ `node_modules/npm/bin/npm-cli.js`. What that does not claim, because a security
408
+ note that overstates its reach is worse than none: anyone who can both set your
409
+ environment and create a file at that path is still obeyed — and anyone able to
410
+ do both can usually substitute node itself. The narrowing is from "any writable
411
+ path" to "a path that looks like a real npm installation": **it stops accidents,
412
+ not an attacker**, and it is not a privilege barrier. An earlier draft said
413
+ "stray, mistaken or opportunistic", which the sentence after it contradicted — an
414
+ opportunistic value costs one `mkdir -p`.
415
+
416
+ - **`npmCliPath()` refused to find npm on a Homebrew Mac.** Its first draft knew
417
+ only the `../lib/node_modules/…` layout, which is what the GitHub-hosted runner
418
+ and nvm use — so it passed in CI and failed on a developer's machine, where
419
+ Homebrew puts npm under `../libexec/lib/node_modules/…`. Both layouts are tried
420
+ now, each from the given path and again through `realpathSync`, since a node
421
+ reached by a symlink resolves its siblings from the real location. Caught by the
422
+ new self-test on the first run, which is the argument for having written it.
423
+
424
+ - **The false-positive fix opened a worse false negative, and the second review
425
+ round caught it.** Excluding `RE.exec('git')` by putting a `(?<!\.)` lookbehind
426
+ in front of the whole alternation also excluded every *member-expression* spawn —
427
+ `child_process.execFileSync('git', …)`, `cp.execSync('git status')` — which is a
428
+ shape SonarJS does report and a contributor can write without doing anything
429
+ unusual. So a fix aimed at a shape nothing writes blinded the sweep to a shape
430
+ people do. The lookbehind now applies to the bare name `exec` alone, measured
431
+ over twelve shapes: the all-names form was wrong on four of them, this one on
432
+ none. The one trade it still makes — `obj.exec('git')`, a method named `exec` on
433
+ something that is not a regular expression — is in the limits list, because no
434
+ expression can tell it from `RE.exec('git')` without a parser. Both directions
435
+ are pinned in the preconditions now rather than left incidental, since a review
436
+ round changed this behaviour by accident once already.
437
+
438
+ Two smaller things fell out of it. The comment describing the lookbehind said
439
+ `(?!\.)` where the code says `(?<!\.)` — a look*ahead* there would match nothing
440
+ useful, so it was the one comment in the file that would mislead somebody
441
+ "fixing" the code to match it. And the worked examples added to document the
442
+ twelve shapes turned the file red, because an example of a bare call, spelled as
443
+ one, *is* a bare call as far as the sweep is concerned; they carry no quote
444
+ characters now. The sweep catching its own documentation is the least ambiguous
445
+ evidence available that the member-expression form works.
446
+
447
+ - **Two documents that enumerate the gates had gone stale in the same commit that
448
+ added one.** `CONTRIBUTING.md` said "four more checks exist, and CI runs all
449
+ four" — five now, and the new one was missing from the block a contributor is
450
+ sent to, which also made the next paragraph's "a fifth command" read as a
451
+ contradiction. The pull-request checklist named two gates as "the
452
+ public-repository gates", so a contributor following it would not have run the
453
+ sweep that exists to catch exactly the call they might be adding.
454
+
6
455
  ## [1.3.15] - 2026-09-27
7
456
 
8
457
  The first release with features in it since the bridge got code. Three
@@ -574,6 +1023,9 @@ for it, so the number is free; the bridge will use it when the platform does.
574
1023
  commits, and a squash makes them unreachable from `main` - which would turn
575
1024
  the check red on `main` for everybody, for something no contributor did.
576
1025
 
1026
+ [1.3.22]: https://github.com/mavrovde/viafrei-bridge/releases/tag/v1.3.22
1027
+ [1.3.16]: https://github.com/mavrovde/viafrei-bridge/releases/tag/v1.3.16
1028
+ [1.3.15]: https://github.com/mavrovde/viafrei-bridge/releases/tag/v1.3.15
577
1029
  [1.3.12]: https://github.com/mavrovde/viafrei-bridge/releases/tag/v1.3.12
578
1030
  [1.3.10]: https://github.com/mavrovde/viafrei-bridge/releases/tag/v1.3.10
579
1031
  [0.0.9]: https://github.com/mavrovde/viafrei-bridge/releases/tag/v0.0.9
package/LICENSE CHANGED
@@ -210,7 +210,8 @@ bridge in this repository.
210
210
 
211
211
  Data obtained through the ViaFrei MCP server keeps its own provider licence and
212
212
  is not relicensed by anything here. Several of those licences are share-alike
213
- (DELFI public-transport data is CC BY-SA 4.0) or purpose-limited (MTS-K fuel
213
+ (DELFI public-transport data is Creative Commons Attribution-ShareAlike; its
214
+ catalogue entry states no version — see SOURCES.md) or purpose-limited (MTS-K fuel
214
215
  prices are consumer information only, with no redistribution in any form,
215
216
  aggregates included).
216
217
 
package/NOTICE CHANGED
@@ -10,15 +10,31 @@ keeps its provider's own terms. Those terms travel with the answer, not with
10
10
  this package, and several of them are share-alike or purpose-limited. In
11
11
  particular:
12
12
 
13
- - DELFI public-transport data is CC BY-SA 4.0. Share-alike: anything derived
14
- from it inherits the obligation, and it must not be blended into a result
15
- carrying a different licence.
13
+ - DELFI public-transport data is Creative Commons Attribution-ShareAlike. The
14
+ catalogue record for the realtime feed names no version (see SOURCES.md), so
15
+ treat the share-alike as applying and do not rely on a version for a
16
+ derivative. Share-alike reaches ADAPTATIONS, not aggregations: if you
17
+ recompute, reshape or rearrange the data you must license that under BY-SA -
18
+ art. 1(a) of the 4.0 text names material "translated, altered, arranged,
19
+ transformed, or otherwise modified", so a rearrangement is inside the
20
+ obligation and not outside it - but showing it beside another source's data
21
+ is an aggregation and puts no obligation on the other source. Which version
22
+ applies is the open question SOURCES.md sets out; the words above are quoted
23
+ from 4.0 because that is the text we read.
16
24
  - MTS-K fuel prices (Tankerkönig) are consumer information only. No
17
25
  redistribution in any form, aggregates and derived tables included.
18
- - Address results are derived from OpenStreetMap under the ODbL 1.0. The
19
- share-alike is on the database rather than on the sentence, and our offer
20
- under section 4.6 stands: ask and you get the extract and our alterations
21
- to it under the same licence.
26
+ - Address, point-of-interest and place results can be derived from
27
+ OpenStreetMap under the ODbL 1.0. What decides it is which TABLE answered,
28
+ not which tool you called: place resolution falls through our own gazetteer
29
+ to the OSM tables, so any answer about a place may be OSM-derived. Read
30
+ `_meta.sources` and the attribution line on the answer you actually got — an
31
+ OSM-derived result names `osm`. The share-alike is on the database (ODbL 4.4)
32
+ and not on the answer (ODbL 4.5(b)); the answer carries a notice instead
33
+ (ODbL 4.3). Our offer under ODbL 4.6 stands and covers BOTH extracts —
34
+ addresses and points of interest, one file each — plus our alterations to
35
+ them, under ODbL 1.0 and free of charge. Ask by opening an issue on this
36
+ repository, or by the e-mail address in the Impressum at
37
+ https://viafrei.de/impressum if you have no GitHub account.
22
38
  - Autobahn GmbH, BKG, DWD, Deutsche Bahn, GeoNames and the AFIR charging
23
39
  feeds each carry their own licence and attribution string.
24
40
 
package/README.md CHANGED
@@ -97,11 +97,18 @@ lorry parking. The table said "car and lorry parking" until this release; it was
97
97
  the same over-claim the Rail row carried about disruptions, and it is corrected here rather
98
98
  than left for a user to discover.
99
99
 
100
- **No tool names, descriptions or schemas are pasted on this page, on purpose.**
101
- This file is frozen inside a published tarball and cannot be corrected without a
102
- release, so a copied catalogue would start rotting the first time a description
103
- changes on the server. There is one source of truth and it is the running
104
- server: connect any MCP client and call `tools/list`.
100
+ **No tool names, descriptions or schemas are written by hand on this page, on
101
+ purpose.** This file is frozen inside a published tarball and cannot be corrected
102
+ without a release, so a hand-copied catalogue would start rotting the first time a
103
+ description changed on the server.
104
+
105
+ **[API.md](API.md) is the way round that, and it is honest about what it is.** It
106
+ is *generated* from [`catalogue.json`](catalogue.json) — a snapshot of what the
107
+ production server answered when asked to describe itself — and CI fails if the two
108
+ have drifted, so the document cannot quietly disagree with the snapshot. What that
109
+ does not fix is the snapshot ageing relative to the live server: a capture is a
110
+ point in time, and API.md's header carries the date it was taken. **The source of
111
+ truth is still the running server**: connect any MCP client and call `tools/list`.
105
112
  (<https://viafrei.de> is the live national traffic digest, not a catalogue.)
106
113
 
107
114
  ## Connect in one line
@@ -147,6 +154,25 @@ Claude Desktop's `claude_desktop_config.json`:
147
154
 
148
155
  Restart the client and ask it one of the questions above.
149
156
 
157
+ ## Documentation
158
+
159
+ | | |
160
+ |---|---|
161
+ | **[API.md](API.md)** | Every tool with its parameters, types, defaults and constraints, plus the resources, resource templates and prompts. Generated from a dated snapshot of the running server, so the descriptions are the server's own words — which is what your assistant actually reads when it picks a tool. |
162
+ | **[Wiki](https://github.com/mavrovde/viafrei-bridge/wiki)** | The prose half: [connecting your assistant](https://github.com/mavrovde/viafrei-bridge/wiki/Connecting-your-assistant), [tools at a glance](https://github.com/mavrovde/viafrei-bridge/wiki/Tools-at-a-glance), the [roadmap](https://github.com/mavrovde/viafrei-bridge/wiki/Roadmap) and an [FAQ](https://github.com/mavrovde/viafrei-bridge/wiki/FAQ). |
163
+ | **[SOURCES.md](SOURCES.md)** | Every publisher, what it covers, its licence, and the attribution line to reproduce — including the conditions that are licence breaches rather than style problems. |
164
+ | **[SUPPORT.md](SUPPORT.md)** | Where a question, a bad answer or a security report should go, and what makes a report easy to act on. |
165
+
166
+ ### Worked use cases
167
+
168
+ Five walkthroughs, each naming the tools that answer it and what comes back:
169
+
170
+ - [Driving Munich to Berlin](https://github.com/mavrovde/viafrei-bridge/wiki/Use-case-Driving-Munich-to-Berlin) — briefing a motorway run: several A-roads in one call, roadworks ahead, a fuel or charging stop, parking at the far end.
171
+ - [The commute that broke](https://github.com/mavrovde/viafrei-bridge/wiki/Use-case-The-commute-that-broke) — departures, regional disruption, and a station lift that is out, which is the difference between a step-free route existing and not.
172
+ - [An EV on a long weekend](https://github.com/mavrovde/viafrei-bridge/wiki/Use-case-An-EV-on-a-long-weekend) — charging by connector and power, low-emission-zone rules, what is around a stop.
173
+ - [Fleet and logistics briefings](https://github.com/mavrovde/viafrei-bridge/wiki/Use-case-Fleet-and-logistics-briefings) — a dispatcher's morning brief, watches that report a change instead of being polled, and the one licence rule that bites hardest here.
174
+ - [Building a local guide agent](https://github.com/mavrovde/viafrei-bridge/wiki/Use-case-Building-a-local-guide-agent) — a vague place to coordinates and back, and what OpenStreetMap's licence asks of you.
175
+
150
176
  ## Why build on it
151
177
 
152
178
  - **One endpoint instead of a stack of integrations.**
@@ -286,9 +312,16 @@ licence breach rather than a style problem:
286
312
  - **MTS-K fuel prices are consumer information only.** No redistribution in any
287
313
  form — that includes aggregates, comparisons, price tables and anything
288
314
  derived. Answer the person who asked; do not build a product out of it.
289
- - **DELFI public-transport data is CC BY-SA 4.0.** Share-alike travels with
290
- anything derived from it, and it must not be blended into a result under a
291
- different licence.
315
+ - **DELFI public-transport data is Creative Commons
316
+ Attribution-ShareAlike.** Share-alike travels with anything you *derive* from
317
+ it — recompute it, reshape it, rearrange it, build a delay table out of it, and
318
+ that is Adapted Material you must license under BY-SA (art. 1(a) names material
319
+ "translated, altered, arranged, transformed, or otherwise modified"). Merely **showing** it beside
320
+ another source's data is an aggregation, and puts no obligation on the other
321
+ source; an earlier version of this page said otherwise, which told you a
322
+ licence forbids something it permits. The catalogue record for the realtime
323
+ feed names no licence version, so do not rely on one for a derivative —
324
+ [SOURCES.md](SOURCES.md) has the detail and the open question.
292
325
 
293
326
  Everything else — where each answer comes from, and what each licence asks of
294
327
  you — is in [SOURCES.md](SOURCES.md). See also [NOTICE](NOTICE) and