Skip to content

feat(notices): validated contract for host-supplied notices - #2666

Open
camielvs wants to merge 3 commits into
banners-02-primitivesfrom
banners-03-contract
Open

feat(notices): validated contract for host-supplied notices#2666
camielvs wants to merge 3 commits into
banners-02-primitivesfrom
banners-03-contract

Conversation

@camielvs

@camielvs camielvs commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Why

An embedding page needs a way to put a notice in front of users — a maintenance window, a migration deadline — without waiting on one of our releases. That means reading data we don't control, so this PR is only the boundary, reviewable before anything renders it.

What

A host may install a notice source on the window. This module reads it and declines to trust it:

  • every field is validated and normalised — anything missing gets a sane default
  • a malformed entry is dropped rather than half-rendered
  • a source declaring a version we don't understand is ignored wholesale, not best-effort parsed
  • action links are narrowed to absolute http(s) URLs

Nothing imports this yet: no UI, no runtime surface. The test file is the argument — it walks through what the boundary does and doesn't let through, including the shapes we'd rather not crash on.

Reviewer notes

Standalone behaviour is unchanged, and stays that way. With no source installed, this returns an empty list forever, so a clean checkout behaves exactly as it does today.

knip.json gains an ignore entry, because the module has no importer until PR 4. The old announcements entry stays until the file it names is actually deleted, also in PR 4.

Copy link
Copy Markdown
Collaborator Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown

🎩 Preview

A preview build has been created at: banners-03-contract/4fb20f2

Comment thread src/config/banners.ts Outdated
Comment thread src/config/banners.ts Outdated
Comment thread src/config/notices.ts
Comment thread src/config/bannerTestSource.ts Outdated
Comment thread knip.json Outdated
Comment thread src/config/banners.ts Outdated
@camielvs
camielvs force-pushed the banners-02-primitives branch from dc471e3 to ac56d12 Compare August 26, 2026 23:45
@camielvs
camielvs force-pushed the banners-03-contract branch from a100783 to eb0c6de Compare August 26, 2026 23:45
camielvs and others added 3 commits August 26, 2026 18:13
Adds the boundary an embedding page talks to: it may install a notice source on
the window, and this module reads it defensively. Every field is validated and
normalised, a malformed entry is dropped rather than trusted, and a source that
declares an unsupported version is ignored wholesale.

Headless and unreferenced — nothing renders these yet. Reviewable on its own,
with the test file as the argument for what the boundary does and does not let
through.
Collapse duplicate ids to the first occurrence and cap a snapshot at 20
entries, so a malformed host cannot produce duplicate React keys or
unbounded per-render validation work.

Freeze each banner rather than only the array — a mutated banner used to
poison the signature cache for the life of the page, since the signature
is computed before a consumer can reach the object.

Declare __TANGLE_BANNER_SOURCE__ as unknown, which is what it is, and the
cast in the test source helper disappears.

Add BANNERS.md documenting the host-facing contract, in particular that
the tangle:banner-source event is required after assigning the global.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Notices are the domain concept; banners are one of the two surfaces they
are shown on. Renames the config module, its global and event, and the
contract doc to match, before any host depends on the old names.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@camielvs
camielvs force-pushed the banners-02-primitives branch from ac56d12 to 3a5d8f1 Compare August 27, 2026 01:18
@camielvs
camielvs force-pushed the banners-03-contract branch from d9ad21b to 4fb20f2 Compare August 27, 2026 01:18
Comment thread src/config/notices.ts

declare global {
interface Window {
__TANGLE_NOTICE_SOURCE__?: unknown;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is typed to unknown but guarded by isNoticeSource, so only TangleNotice will work. TangleNotice is Tangle-UI's expectation of the blind contract and thus this is the shape the source will need to provide.

@camielvs
camielvs marked this pull request as ready for review August 28, 2026 00:02
@camielvs
camielvs requested a review from a team as a code owner August 28, 2026 00:02
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.

1 participant