The gap
The notes-list grammar (`parseNotesQueryOpts`, `src/routes.ts`) can include by path — `path` (exact) and `path_prefix` — and can exclude by tag (`exclude_tag`, core `excludeTags`), but there is no path exclusion. A client that wants "notes NOT under `.parachute/`" cannot say it; the negation half of any path predicate must be applied client-side after downloading rows.
Where this bit
parachute-app's setup-shelf boolean ("does a user-authored note exist") is defined as NOT `#guide` AND NOT path-prefix `.parachute/` (`src/lib/home/checklist.ts`). The tag half pushes server-side via `exclude_tag=guide`; the path half can't, so the cheap form of the check — `exclude_tag=guide&limit=1` (~403 B) — is wrong: the app's own settings note (`.parachute/notes/settings`, rewritten on settings changes, so recently-touched on precisely the vaults that aren't onboarded) comes back first and reports "onboarded" on a vault the user never wrote in. The app ships a paged client-side filter as the workaround (app#110 Finding B); with a path-exclusion param it collapses to one `limit=1` round-trip with zero client reasoning.
This is the same family as #626/#627 — a question the server can't answer, so the client answers it by downloading rows — but narrower: the grammar already has both halves of the machinery (path matching, exclusion semantics), just not composed.
Shape
`exclude_path_prefix` (repeatable, same list-parsing as `exclude_tag`) mirroring `path_prefix`'s matching exactly, on both `/api/notes` and `/api/subscribe` — the shared `parseNotesQueryOpts` means the predicate-parity property holds by construction. `.parachute/` system-space exclusion is likely the first use every surface wants; agents excluding their own working directories (`Uni/`, `Jobs/`-style territories) is the second.
The pattern, named
#626 (no cheap count), #627 (no date range), and this are three instances of one shape found in a single measurement pass: the server can't express the question, so the client downloads the corpus and answers it locally. Each workaround has its own silent cliff (limit=5000 stops meaning "everything" at note 5,001; the limit=1 shortcut above corrupts semantics). Three one-off params would close these three; the pattern is the better argument — a filter grammar rich enough that "count it," "bound it by date," and "exclude this namespace" are expressible questions, so the next surface doesn't discover instance four by shipping it.
The gap
The notes-list grammar (`parseNotesQueryOpts`, `src/routes.ts`) can include by path — `path` (exact) and `path_prefix` — and can exclude by tag (`exclude_tag`, core `excludeTags`), but there is no path exclusion. A client that wants "notes NOT under `.parachute/`" cannot say it; the negation half of any path predicate must be applied client-side after downloading rows.
Where this bit
parachute-app's setup-shelf boolean ("does a user-authored note exist") is defined as NOT `#guide` AND NOT path-prefix `.parachute/` (`src/lib/home/checklist.ts`). The tag half pushes server-side via `exclude_tag=guide`; the path half can't, so the cheap form of the check — `exclude_tag=guide&limit=1` (~403 B) — is wrong: the app's own settings note (`.parachute/notes/settings`, rewritten on settings changes, so recently-touched on precisely the vaults that aren't onboarded) comes back first and reports "onboarded" on a vault the user never wrote in. The app ships a paged client-side filter as the workaround (app#110 Finding B); with a path-exclusion param it collapses to one `limit=1` round-trip with zero client reasoning.
This is the same family as #626/#627 — a question the server can't answer, so the client answers it by downloading rows — but narrower: the grammar already has both halves of the machinery (path matching, exclusion semantics), just not composed.
Shape
`exclude_path_prefix` (repeatable, same list-parsing as `exclude_tag`) mirroring `path_prefix`'s matching exactly, on both `/api/notes` and `/api/subscribe` — the shared `parseNotesQueryOpts` means the predicate-parity property holds by construction. `.parachute/` system-space exclusion is likely the first use every surface wants; agents excluding their own working directories (`Uni/`, `Jobs/`-style territories) is the second.
The pattern, named
#626 (no cheap count), #627 (no date range), and this are three instances of one shape found in a single measurement pass: the server can't express the question, so the client downloads the corpus and answers it locally. Each workaround has its own silent cliff (
limit=5000stops meaning "everything" at note 5,001; thelimit=1shortcut above corrupts semantics). Three one-off params would close these three; the pattern is the better argument — a filter grammar rich enough that "count it," "bound it by date," and "exclude this namespace" are expressible questions, so the next surface doesn't discover instance four by shipping it.