Skip to content

Commit 0c5108e

Browse files
committed
docs(github): record why the strict guards stop at writes
The TSDoc stated the boundary and the harm on a write, but not why reads are deliberately excluded. Without that, the asymmetry reads as an unfinished pass and the next contributor "completes" it, breaking a paste flow that works today for no safety gain. Records both halves of the reasoning: the harm is asymmetric (a write mutates a resource the caller never named, unrecoverably and invisibly, since every traversal assertion still passes; a read returns data from the resource they almost certainly meant), and the cost of refusing runs the other way (a padded id on a read is overwhelmingly a stray newline in a paste). States the principle underneath both guards: refuse where being wrong is unrecoverable, tolerate where being wrong is merely unhelpful. Comment-only; no behaviour change.
1 parent d2c74d7 commit 0c5108e

1 file changed

Lines changed: 21 additions & 4 deletions

File tree

apps/sim/tools/url-path.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,27 @@ function assertUnpadded(value: string | number | bigint, paramName: string): voi
422422
* read `params.gist_id?.trim()` — keeps trimming, because preserving its
423423
* behaviour is the same rule, not an exception to it.
424424
*
425-
* Reads deliberately keep {@link safeUrlPathSegment}. Trimming a padded id on a
426-
* GET is the copy-paste convenience that helper exists for, and its worst case
427-
* is returning data the caller can simply ignore — not destroying a branch,
428-
* closing someone's pull request, or filing an issue in a real repository.
425+
* **Reads deliberately keep {@link safeUrlPathSegment}, and that asymmetry is
426+
* the point rather than an unfinished pass.** Do not "complete" it by routing
427+
* GET routes through this guard — doing so breaks a flow that works today and
428+
* buys no safety. The reasoning, since this is the first question the boundary
429+
* invites:
430+
*
431+
* The rule above is "never turn a failing request into a succeeding one", but
432+
* the *reason* the rule exists is that the harm is asymmetric. On a write, the
433+
* failure mode is destroying or mutating a resource the caller never named —
434+
* unrecoverable, and invisible in review because every traversal assertion
435+
* still passes. On a read, the failure mode is returning data from the resource
436+
* the caller almost certainly did mean, since they typed the padded name
437+
* themselves; the worst case is data they ignore.
438+
*
439+
* The *cost* of refusing runs the other way. A padded identifier arriving at a
440+
* read is overwhelmingly a paste carrying a stray newline, so rejecting it
441+
* breaks a working flow for no gain. On a write, rejecting costs the caller one
442+
* clear error message and saves a branch.
443+
*
444+
* So the principle underneath both guards is: **refuse where being wrong is
445+
* unrecoverable, tolerate where being wrong is merely unhelpful.**
429446
*
430447
* @param value - The raw identifier, typically LLM- or user-supplied.
431448
* @param paramName - The parameter name, used to name the offender in errors.

0 commit comments

Comments
 (0)