Skip to content

fix(navigation): ignore unknown query parameters on Buzz links and remove the buzz://open locator - #457

Merged
wesbillman merged 3 commits into
mainfrom
deep-links-part-3
Sep 30, 2026
Merged

wesbillman merged 3 commits into
mainfrom
deep-links-part-3

Conversation

@matt2e

@matt2e matt2e commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Third deep-links slice: three commits on top of main at 5d2b08e.

  • Ignore unknown query parameters on Buzz links (ed16fc2). buzz://message keeps only channel, id and thread; the channel forms ignore the whole query; entity hosts keep only their per-type keys, so commit on a project or tab on a PR/issue is dropped too. A duplicated known key such as channel=a&channel=b is still ambiguous and rejects. This matches the original Buzz desktop client's lax message-link handling. Credentials, port, fragment, the length cap and the lowercase scheme requirement are unchanged. Everything rebuilt from a parsed link (entityHref, Copy link, the browser address) already emits the canonical form.
  • Pin the project commit-hash drop (40c32b4). Test-only: buzz://project?…&tab=commits&commit=<hash> parses to the commits section with no hash, and entityHref of the result is the canonical project link.
  • Remove the buzz://open?target= locator codec (fbc3c57). SharedTarget, parseSharedTarget, bindSharedTarget, targetLink, parseTargetLink and the shared link format are deleted, with no shim or flag. parseBuzzLink returns null for buzz://open, so it is plain text in message content and the OS ingress fails it as invalid-target through the same null check as any other unknown host. The original Buzz client never had this form: it was introduced in Add navigation history and preserve Messages sidebar on return #18, refused at the OS boundary in feat(desktop): add Buzz deep links and read-only entity destinations #208, and is now removed. The #buzz= browser address, parseOpenTarget and browser-history.ts share the JSON target shape, not the URL codec, and are untouched. Link Lab demonstrates the same in-app links with buzz://channel and buzz://message samples; docs/deep-links.md, docs/plugin-architecture.md and the Link Lab README now describe unknown hosts as rejected everywhere rather than "in-app only".

Behaviour change

  • Buzz links that another client decorated with extra query parameters now open instead of failing.
  • Existing in-app buzz://open links in old messages stop opening and lose their preview and channel label.
  • @buzz/author no longer exports the SharedTarget type. No in-repo plugin imported it.

Verification

  • tsc --noEmit, pnpm lint and pnpm check are clean.
  • Full Vitest suite: 455 files, 5578 tests passed.
  • Chromium journeys tests/browser/buzz-links.spec.mjs and tests/browser/entity-destinations.spec.mjs: 5 passed.
  • One negative test per parser layer pins the removal with a literal buzz://open?target=… string: parseBuzzLink returns null, deepLinkStep fails invalid-target, messageLinkParts leaves it as plain text, and InlineLink renders it as text.
  • A grep of src/, tests/, docs/ and the README finds buzz://open only in those negative tests.

Known follow-up

Open #401 adds a test in src/app/entity-navigation.test.tsx that calls bindSharedTarget(parseTargetLink(targetLink(…))). It will conflict with this branch and needs rewriting against parseOpenTarget, whichever lands second.

🤖 Generated with Claude Code

matt2e and others added 3 commits September 30, 2026 17:19
Buzz links now drop any query parameter outside their grammar, values and
repeats included, instead of failing as invalid-target: `message` keeps only
channel, id and thread, the `channel` forms ignore the whole query, and the
entity hosts keep only their per-type keys, so `commit` on a project and `tab`
on a PR or issue are ignored too. A duplicated known key such as
channel=a&channel=b is still ambiguous and rejects.

This matches the original Buzz desktop client's lax message-link handling
while staying strict on duplicated known keys. Credentials, port, fragment,
the length cap, the lowercase scheme requirement and the OS-boundary refusal
of buzz://open are unchanged. Everything rebuilt from a parsed link
(entityHref, Copy link, the browser address) already emits the canonical form
without the extra parameters.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
…r strictness

Follow-up to ed16fc2 with no behaviour change. The routes test now covers
`buzz://project?owner=…&d=…&tab=commits&commit=<hash>`, the one lax shape where
the ignored value looks meaningful: it parses to the project's commits section
with no commit hash, and `entityHref` of the result is the canonical project link
with `tab=commits` only.

docs/deep-links.md now says the unknown-parameter rule does not extend to
`buzz://open?target=…` locators, which still require exactly one `target` key,
so readers do not assume every Buzz link became permissive.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
`buzz://open?target=<json>` is no longer a Buzz link anywhere in buzz-app.
`parseBuzzLink` returns null for it, so in message content it is plain text
like any other unknown host, and the OS ingress fails it as `invalid-target`
through the same null check as `buzz://join` or `buzz://unknown` instead of a
dedicated refusal. The codec itself is gone from targets.ts: `SharedTarget`,
`parseSharedTarget`, `bindSharedTarget`, `targetLink` and `parseTargetLink`,
along with the `shared` format in buzz-links.ts and the `LocatorScope` type it
existed for; `OpenTarget` is now the only target shape and `parseOpenTarget`
binds scopes directly.

Behaviour change: existing in-app `buzz://open` links in old messages stop
opening and lose their preview and channel label, and `@buzz/author` no longer
exports the `SharedTarget` type. No in-repo plugin imported it. The `#buzz=`
browser address, `parseOpenTarget` and browser-history.ts are untouched; they
share the JSON target shape, not the URL codec. Copy link already emitted
`buzz://message`, so its comment is the only change there.

The original Buzz desktop client never had this form. It was introduced in
PR #18, refused at the OS boundary in PR #208, and is now removed. MessageLink
and ReferenceText keep their message and channel handling with the type
narrowing simplified; channelForLink/channelLinkLabel drop the scope parameter
they only used for locator community checks. Link Lab demonstrates the same
in-app links with `buzz://channel/general` and a `buzz://message` thread link,
and docs/deep-links.md, docs/plugin-architecture.md and the Link Lab README
describe unknown hosts as rejected everywhere rather than in-app only.

One negative test per parser layer pins the removal with a literal
`buzz://open?target=…` string: parseBuzzLink returns null, deepLinkStep fails
`invalid-target`, message-link-parts leaves it as plain text, and InlineLink
renders it as text. The open PR #401 adds a test in entity-navigation.test.tsx
that calls bindSharedTarget/parseTargetLink/targetLink; it will conflict with
this commit and needs rewriting against parseOpenTarget when it lands.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
@matt2e
matt2e requested review from a team, comp615 and wesbillman as code owners September 30, 2026 12:34

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No actionable findings in the unknown-parameter handling or locator removal; the supported links still bind through the existing navigation admission path, and retired locators become inert. Star Lord automated source review via Wes’s account: head fbc3c57f055641664c527e72365679dc8a7c30d9, base 5d2b08e2ff3bb40dc62f04015298ff319f4a4f8a. No tests or app execution; hosted CI was still running at the snapshot, and packaged deep-link delivery plus keyboard/focus recovery remain unverified.

no community: they bind to the receiving conversation's community and viewer and
pass through existing navigation admission and session ownership checks. The app
has no link form of its own; any other `buzz://` host is rejected everywhere,
staying plain text in messages. Message targets open their

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 [P3] Distinguish unknown hosts from supported entity links

After listing only the channel/message forms, this passage says any other buzz:// host is rejected everywhere and stays plain text. Supported buzz://repo, buzz://project, buzz://pr, and buzz://issue links still pass through parseBuzzLink and are linkified/routed. For example, messageLinkParts("buzz://repo?owner=<valid-hex-owner>&d=r") returns a link part; the existing InlineLink test also expects repo links to have kind buzz.

Please use “any unknown buzz:// host” (as in docs/deep-links.md) or reference the full accepted-forms list so plugin authors are not told supported entity links are inert. The same wording needs correction in src/bundled/link-lab/README.md:35–37.

@wesbillman
wesbillman merged commit 6de1ab4 into main Sep 30, 2026
20 checks passed
@wesbillman
wesbillman deleted the deep-links-part-3 branch September 30, 2026 16:25
TheSentinel454 pushed a commit that referenced this pull request Sep 30, 2026
* origin/main: (27 commits)
  Let plugin pages publish NIP-AR artifacts and embed the host thread view (#434)
  test(app): migrate entity-navigation test off removed buzz://open locator API (#463)
  Show agent activity in navigation (#423)
  test(browser): hold motion when it commits, not on its start event (#459)
  fix(navigation): ignore unknown query parameters on Buzz links and remove the buzz://open locator (#457)
  feat(design-system): distinguish controls on floating surfaces (#429)
  feat(native): add community extras and media preparation (#450)
  Clone inventory identities through reviewed text and fresh identity creation (#289)
  feat(communities): add right-click actions to the community rail (#400)
  fix(messages): keep a send reveal pending until its scroll runs (#454)
  fix(messages): reserve a stable scrollbar gutter on the channel feed (#451)
  fix(sidebar): list plugin pages as sidebar rows via an opt-in primary flag (#401)
  feat(channels): surface canvas content in channel settings (#426)
  fix(profiles): remove redundant presence status row (#394)
  test(browser): count live retries once the page handles startup controls (#443)
  feat(composer): host-owned resource links for the Projects picker (#445)
  feat: support native read state and recent channel activity (#444)
  feat(native): serve relay media and uploads in packaged builds (#433)
  feat(channels): suggest joined channels in the composer (#446)
  feat: support native agent activity, library, memories, and community resolution (#441)
  ...

Signed-off-by: Codex <noreply@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants