Skip to content

Tags: raystack/chronicle

Tags

v0.16.3

Toggle v0.16.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: bump fumadocs-core to 16.15.15 so bold text does not overflow th…

…e stack (#186)

mdast-util-to-markdown 2.1.3 serialises strong and emphasis through
containerPhrasing, which reads an `attention` property off the handler.
fumadocs-core's stringifier wraps every handler in a new function and,
before 16.15.15, dropped that property. The strong handler then calls
containerPhrasing, which calls the wrapped strong handler again, until
the build fails with "Maximum call stack size exceeded" on any page with
bold or italic text.

fumadocs-core pins mdast-util-to-markdown with a caret, so every fresh
install of chronicle picks up 2.1.3 and breaks. 16.15.15 copies the
handler's properties onto the wrapper.

The lockfile moves mdast-util-to-markdown to 2.1.3 as well, so CI builds
against the version a fresh install resolves.

v0.16.2

Toggle v0.16.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs: rebuild the documentation (#185)

* fix: keep navigation inside the section it belongs to

A content directory is a section: its own folder, its own URL prefix, its own
navigation. Four things were leaking across those boundaries or losing them.

`filterPageTreeByContentDir` was called twice on the same tree — once by
`entry-server` before serialising, once by `DocsLayout` on what it was handed —
and the second pass had no way to tell an already narrowed tree from a wide one.
It looked for a folder whose urls all start with the prefix, which on a narrowed
tree matches the first sub-folder instead. So the docs site rendered an empty
sidebar, the basic example showed only its `guides` folder, and a versioned site
showed only its first content directory.

Telling the two apart needs three signals, because url shapes alone are
ambiguous: `root → [folder Docs (/docs/*)]` and
`root → [folder guides (/docs/guides/*)]` look identical to a prefix test. A
folder is the content directory when every url inside it belongs to the
directory, when it holds every url in the tree that belongs to the directory,
and when it has a page directly below the prefix rather than only pages nested
deeper. A page sitting at the prefix settles it earlier: that is the directory's
own page, so the tree is already its contents.

`filterPageTreeByVersion` had the same double-call problem and now recognises an
already narrowed tree too.

Previous and next were chained across the whole site, so the last page of Docs
offered the first page of Ops Guide — walking a reader out of the section they
chose. Both implementations now chain per section, sharing
`contentSectionPrefixes` and `sectionOf` so the server and the static build
cannot drift apart.

The fanfold header printed "DOCS / DOCS / GETTING STARTED": `getBreadcrumbItems`
starts at the tree root, whose name is the section's own label. Any name that
repeats the one before it is dropped, which also covers a folder whose index
page carries the folder's title. Its workaround for the scoping bug goes with
it — the filters are safe to apply twice now.

* fix: give every page one title

`RootHead` rendered the site title and every route then rendered its own, so two
`<title>` tags reached the document. A browser reads the first one, and
`RootHead` renders before the page — so every tab in the site said "Chronicle"
no matter which page was open.

Dropped the one in `RootHead`, which keeps the site-level JSON-LD and nothing
else. Every real route already renders `<Head>`; the two that did not were the
404 and the render-error page, which showed whatever title the previous page had
left behind. Both name themselves now.

* fix: build sites that declare versions

`chronicle build` failed on any site with a `versions:` key, so no versioned
site could ship. Dev mode was fine, which is why it went unnoticed — the error
only appears when the production bundle is assembled.

`remarkResolveImages` located a page's content root by looking for `/content/`
in its path and returned when it found none. Versioned pages live under
`versions/v1/docs/`, so every one of them took that path and returned before
setting `file.data.images`. The mdx config names `images` in `valueToExport`, so
the build then died with a missing-export error on the first versioned page it
reached.

It now recognises `versions/` as well. The path below the marker is also the
path under `/_content/` — `content/docs/` mirrors as `docs/` and
`versions/v1/docs/` as `v1/docs/` — so versioned pages get working image
resolution rather than only a build that finishes.

`images` is also initialised before any early return. A page that resolves to
neither root still exports an empty list, so a path shape nobody anticipated
cannot break a build again.

* feat: honour theme.colors

`theme.colors` has been in the config schema, documented as "custom color
overrides", and read by nothing. A site could set it, pass validation, and see
no change.

The alternative was deleting the key, but the schema is `.strict()` — a config
that sets it would go from silently ignored to failing at startup. Better to
make the promise true.

Keys name an Apsara colour token and take the `--rs-color-` prefix
automatically; a key written as a full custom property is used as-is, which is
how a theme's own variables like `--paper-ink` are reached. The declarations go
into the head after the stylesheets so they win, and under `:root` plus both
`[data-theme]` selectors, because Apsara defines its dark values behind that
attribute and a bare `:root` would lose to them once a reader picks a theme.

One value covers both themes, since the config holds one value per token. A
token that needs to differ is better left to the theme.

Values are checked against a colour-shaped pattern before they reach the
stylesheet. This string comes from a config file and is written into CSS, so
anything that could close a declaration or open a rule has to be impossible
rather than unlikely.

* feat: give every theme a logo, and put a folder's index in the sidebar

Four things in the sidebars, all of them about what a reader could not see.

A site that set no `logo` got a book icon in the default theme and nothing at
all in paper or fanfold. The Raystack mark is the fallback now, in all three,
drawn from the brand file in `raystack/website` with `currentColor` so it works
on either ground. `SidebarLogo` moves to `components/ui/logo.tsx` and resolves
`logo.light` / `logo.dark` for every theme, so a site that sets one gets it
everywhere rather than only where a theme happened to look.

In fanfold the mark sits above the site name rather than beside it. The rail is
a fixed 240px and the display face is wide, so sharing a line started breaking
titles mid-word.

A folder's index page never appeared in the sidebar. `SidebarNode` and
`ChapterNav` render a folder's children and its group label is a collapse
trigger rather than a link, so the page existed with no route into it. Both
render it as the group's first row now — which is what fanfold's `Nav` already
did.

Top-level groups were never collapsible, because `collapsible={depth >= 1}` was
written when the content-root wrapper made every real group a level deeper.
Every group can be collapsed now, with top-level ones open on arrival and nested
ones open only when they hold the page being read.

Two spacing corrections fall out of the above. `.navGroup[data-depth='0']` set a
top margin on every top-level group including the first, which doubled the
padding `.sidebarMain` already applies — 48px above the first label. Apsara
guards its own nav-group margin with `:not(:first-child)`; that guard is
restored and the rest of the rule is dropped, since Apsara's spacing was
winning on source order anyway. The content-directory links then needed a margin
of their own: they are a separate block above the tree, and the tree may start
with a plain page carrying no margin, so the two ran together.

* docs: rebuild the documentation around reader tasks

The docs were organised around the framework's own parts. Nine pages, each named
after a thing — Configuration, Frontmatter, Components, Themes — so a reader who
arrived with a job had to guess which noun held the answer. Twenty-four pages in
six groups now, named after the jobs: start here, writing docs, guides, themes,
reference, deploy and operate.

`features.mdx` is gone. It was doing three jobs at once: a marketing bullet
list, a feature index, and the only documentation in the whole site for five
topics. Sorting and `meta.json` went to Navigation, redirects to Links and
redirects, markdown URLs and `llms.txt` to Generated routes, the playground to
API reference, health checks to Monitoring. What was left became an
Introduction.

Versioning, API references and deployment each had a config key and a bullet and
no guide, despite being three of the strongest reasons to pick Chronicle.
Deployment was the sharpest case: the last step of the job was three words in a
config reference. They have guides now, along with search, multiple content
sections, and moving an existing docs site over.

`configuration.mdx` was 468 lines, nearly a third of the site, and was where
people were forced to learn versioning and API specs because there was nowhere
else. It is a reference again, with a link to the guide for each large key. The
frontmatter page leads with a table, and its `draft` section is no longer pasted
into the middle of `authors`, which had split that field's prose in half.

Writing the guides meant reading the source, which turned up four things the
docs had wrong. Only three deploy presets were listed where the code has eight,
four of them static. Search was called "powered by Fumadocs" on one page and
"SQLite FTS5" on another; it is SQLite full-text search on a server build and a
downloaded index in the browser on a static one, which decides how large a
static site can sensibly get. A static build is a single-page app rather than
pre-rendered HTML, so a host has to rewrite unknown paths to `index.html` —
undocumented, and the usual reason a deep link 404s. And it optimises images at
build time, which the old page denied.

Seven permanent redirects cover every URL that moved, so no existing link
breaks. `url` is set, which the sitemap, canonical tags and social cards all
needed and none of them had.

Content elsewhere gets the same treatment as the pages: every file opened with
an `# H1` repeating its own frontmatter title, so both examples and the `init`
scaffold showed the same words twice. Removed, and the rule is written down under
`title` in the frontmatter reference. The one exception is the basic example's
heading-levels demo, which now starts at `##` and says why.

* fix: address review findings, and trim the commentary

CodeRabbit flagged eight things on #185. Seven were real.

The serious one: identifying a content directory by the shape of its urls broke
a section that holds only sub-folders. No index page and no page directly under
the prefix meant the wrapper failed the depth check, and with a sibling section
present the tree came back empty — an empty sidebar.

The heuristic is gone. `buildFiles` already marks every content root with
`root: true`, and fumadocs carries that onto the folder node; it was only
missing because `compactTree` dropped it before the tree reached a layout.
Keeping it on folders makes the check explicit, and the three-signal guess with
it. `KEEP_FIELDS` gains `root` for folders only — it means nothing on a page or
a separator, and a test already pinned that.

`splitContentRoot` took the first marker in array order rather than the deepest
in the path, so a project inside a directory named `versions` resolved its
images against the wrong root.

Sidebar groups used `defaultOpen`, which is read once on mount. Client-side
navigation does not remount the tree, so a group the reader collapsed stayed
shut over the page they then opened. Controlled now, reopening when it holds the
active page and otherwise leaving the reader's choice alone.

`Logo` gained a `labelled` option. Fanfold and paper render the site name beside
it, where announcing it again is noise — and the fallback is Raystack's mark,
which should not speak for a site that is not theirs.

Four documentation corrections, all mine. The request tester's proxy means an
auth token reaches the docs server, so "stays in their browser" was wrong and
worth being plain about. The search index is built on the first readiness or
search request, not at startup — nothing calls `ensureIndex` before then. A page
without `title` renders as `Untitled` rather than failing, so "required" was
overstating it in three places. And pages do not sort before folders: they share
one scale, as the same page said two sections later.

The eighth finding misread the mark's label as the site title. Skipped.

Comments throughout this branch were doing too much explaining. Cut back to what
the code cannot say for itself.

* feat: fall back to the site's initial, and use the logo as the favicon

The fallback mark was Raystack's. Fine for a Raystack site, wrong for anyone
else's — a project that forgot to set `logo` shipped someone else's branding.
It is now the site's first letter in a tinted box: plain enough to read as
unfinished rather than borrowed. Codepoint-safe, so an emoji or a non-Latin
title survives being taken apart.

Two places ignored `logo` entirely.

The favicon was hardcoded to `/favicon.ico` and `/favicon.svg`, so a site that
set a logo still had to supply favicon files separately, and one that did
neither got no favicon at all. A configured logo is now the icon; the static
files stay as the fallback, so an explicit favicon still works.

Social cards read `logo.dark` alone. A site that set only `light` got a card
with no mark even though its sidebar showed one. Both variants resolve now, and
a site with no logo gets the same initial the sidebar falls back to.

Drawing the box from `currentColor` looked right but was not: Apsara colours its
text components rather than their containers, so the inherited colour is black
in both themes and the letter vanished on a dark ground. It takes explicit
tokens instead. `.brandLogo` in fanfold was setting `display: block` for an svg,
which killed the flex centring — block-level `flex` keeps both.

* docs: use the Raystack mark on Chronicle's own site

Chronicle is a Raystack project, so its docs should carry the Raystack mark —
but through `chronicle.yaml` like any other site would, not baked into a theme.
The themes stay neutral and fall back to the site's initial.

The brand file draws a flat navy through a single-stop gradient, which reads as
near-black on a dark page. Flattened to a plain fill, with a light variant for
`logo.dark`. Same path data either way.

This also becomes the favicon and the mark on social cards, which the config now
covers.

* docs: draw the mark in greyscale

Navy pulled the eye to the one thing on the page that is not type. Greyscale
sits with the rest of the sidebar instead.

Matched to the site's own ink rather than picked by eye:
`--rs-color-foreground-base-primary` is oklch(0.2435 0 0) in light and
oklch(0.9491 0 0) in dark, which are #202020 and #EEEEEE.

v0.16.1

Toggle v0.16.1's commit message
feat: let a page state its own identifiers, and prefer short labels i…

…n the trail

The fanfold header was designed to carry what a page is: the standard it
implements, the package, the command. The theme cannot know any of that, so it
was filling the two lines under the trail with the only facts it had — the site
title, the section, and the URL.

A page can now name them itself. `identifiers` is a list of lines printed
verbatim under the trail, and a page without it keeps the lines the theme can
work out on its own, so nothing else changes.

Frontmatter reaches a theme through one allowlist in `extractFrontmatter`, so
that is the only place a new field can enter — and the only edit needed here
beyond the theme. Non-string entries are dropped rather than rendered: this
comes straight from a content file, where one mistyped entry should not arrive
as `[object Object]`.

Named `identifiers` rather than `meta` because `meta.json` already means a
directory's metadata and the two would read as the same thing.

The trail now prefers a `short` where the tree carries one, so it reads
"… / SPP" rather than "… / Space Packet Protocol". `getBreadcrumbItems` is
fumadocs' own and returns names only, so the lookup is built here from the
`short` that `attachShortNames` puts on every page node and each folder index.
Kept local to this theme: the other two render their breadcrumbs from the shared
helper and never use `short`.

v0.16.0

Toggle v0.16.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat: add the fanfold theme, and a `short` frontmatter field(#181)

Fanfold is a third built-in theme: continuous-form line printer paper, with
tractor-feed strips down both edges, faint zebra banding behind the sheet and
monospace type throughout. It was translated from a design file, so its colours
and metrics come from there rather than from a style guide.

Two changes reach outside the theme.

`short` frontmatter gives navigation a shorter label than the page title:

    title: Space Packet Protocol
    short: SPP

The rail shows `SPP` and keeps the full title on the link's tooltip; headings,
breadcrumbs, the browser tab and search all keep using `title`. `source.ts`
copies it onto the page tree and `tree-utils.ts` exposes a `shortName` reader,
so any theme can pick it up — today only fanfold does, since only its rail is
narrow enough to need it. Note `short` had to be added to `KEEP_FIELDS`: that
allowlist strips unknown fields when the tree is serialised for the client, so
without it the value never reached the browser.

Themes may now supply their own landing page through an optional `Landing` slot.
The shared `LandingPage` still resolves config, `<Head>` tags and the version
label, so a theme's `Landing` is presentation only. Themes that leave it out
keep the existing layout. `LandingEntry` moved to `types/content.ts` so
`types/theme.ts` can name it without closing an import cycle.

Smaller shared changes:

- `useSearch` is exported, so a theme can build its own search trigger instead
  of the stock icon button.
- Departure Mono is declared once in `themes/fonts/` and shared by the paper and
  fanfold themes. Both previously declared the same family from their own copy of
  a byte-identical file, so the build shipped 22KB twice and the two `@font-face`
  rules collided on family name.
- Fanfold requests its web fonts from a `<link>` rather than a stylesheet
  `@import`. `registry.ts` imports every theme statically, so an `@import` was
  hoisted into the one bundled stylesheet and every site fetched Doto and Geist
  Mono — including sites running a different theme that never renders them.

v0.15.1

Toggle v0.15.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: stack sidebar footer as a row in production builds (#180)

v0.15.0

Toggle v0.15.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat: add configurable links to the sidebar footer (#176)

* feat: add configurable links to the sidebar footer

Adds a top-level `links:` key to chronicle.yaml, surfaced from the sidebar
footer in both the default and paper themes.

On desktop the links sit behind a `?` icon button that opens a menu upward,
matching the design. In the default theme's mobile hamburger menu they render
inline as nav items instead, above the version switcher — no dropdown.

Each destination is tagged with a `ref` query param carrying the full URL of
the page the link was clicked from. External links open with `noopener` (not
`noreferrer`) so the destination also receives a Referer header; under the
default referrer policy that header carries only the origin, so `ref` is what
identifies the specific page. Existing query strings on the href are
preserved, and non-web schemes such as mailto: are left untouched.

The sidebar footer previously rendered only when versions were configured; it
now renders when either versions or links are present.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: show latest.label as a static footer label when unversioned

`latest` is valid without `versions` — the schema only requires it in the
other direction — but both VersionSwitchers returned null whenever no
versions were configured, so a configured `latest.label` was silently
dropped.

It now renders as static text in the sidebar footer. Deliberately not a
dropdown: with nothing to switch to, a menu holding a single option would
imply a navigation that does not exist.

The footer visibility check now also accounts for a latest-only config.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: add a mobile layout to the paper theme (#178)

The paper theme had no small-screen branch at all — none of its four CSS
modules contained a single media query. At 390px it rendered the full desktop
layout: the 262px sidebar ate two thirds of the viewport, and `.content`'s
`margin-left: calc(-1 * var(--paper-sidebar-width))` dragged the article
underneath it, clipping headings to "e (3.0)", "st" and "ed". There was no
header, no hamburger and no way to reach the navigation.

Add a mobile layout mirroring the default theme's, at the same 768px
breakpoint:

- a sticky mobile header carrying the existing SidebarHeader, the theme
  switcher and a hamburger toggle
- a full-screen menu holding the chapter nav, a divider, the configured links
  as inline items (SidebarLinks variant='list') and the version switcher
- the desktop sidebar hidden and `.content`'s negative offset neutralised, so
  the article is full-width and unclipped
- the menu closes on navigation

The reading-progress rail is a fixed 200px column pinned to the right edge, so
it is dropped below the breakpoint rather than left overlapping the article,
and the page navbar parks below the header instead of colliding with it.

Two narrow-viewport defects surfaced once the content became full-width: a long
breadcrumb could not shrink past its intrinsic width and stretched the page 7px
wider than the viewport, and a long site title wrapped out of the fixed-height
header. Both are corrected inside the media query.

The mobile chrome only renders when the sidebar does, so it composes with
reader mode rather than fighting it. Desktop rendering is unchanged —
screenshots at 1440x900 before and after are byte-identical.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

v0.14.0

Toggle v0.14.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat: add author pages listing everything an author wrote (#172)

* feat: add author pages listing everything an author wrote

Adds an optional `authors` registry to chronicle.yaml holding each
author's name, bio, avatar, url, and email. A frontmatter string that
matches a registry key picks up that profile; anything else still reads
as a plain name, so occasional contributors need no config.

/authors lists everyone found in the content, and /authors/<slug> shows
one author's profile followed by their pages grouped by content dir. The
index is served from /api/authors, written to /data/authors.json for
static builds, and embedded in the SSR payload so both routes render
without JavaScript. A content dir named `authors` keeps its own pages.

Bylines now use the registry avatar and profile link when present.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: add breathing room above author page content

Also gives the paper-theme example an authors registry entry, so both
themes demonstrate a full profile.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: give author pages the paper theme's sheet and fonts

Author pages bypass each theme's Page component, so in paper they sat
flat on the neutral backdrop while every other page rendered on a sheet.
Tag the page root with the active theme and, for paper, apply the same
sheet treatment its content uses — base background, side borders, soft
shadow — with the theme's Hanuman body face and Departure Mono for the
meta text.

The profile header now stacks as avatar and name on one line, the bio
below it, then the links.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: put the paper byline in the meta line beside reading time

Paper stacked the avatar byline under the page description. Move the
authors up into the meta line as `1min Read | JANE DOE`, inheriting that
line's mono face and tertiary color, with round avatars.

Bylines now show two authors at most and collapse the rest into a `+N`
counter that names them on hover.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: link byline names to the author's page

The byline linked out to an author's `url`, or their email, which sent
readers off-site while the author page it should reach went unlinked.
Names now always go to `/authors/<slug>`, keeping the version prefix and
falling back to plain text when a content dir owns that segment. An
author's url and email stay on their page.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: hide the AI menu on author pages

Author pages aren't MDX, so `/authors/jane.md` 404s and every item in the
menu — copy, view, and both hand-offs — fails silently. Hide it there.

Adds an isAuthorRoute predicate and uses it at the four places that were
each spelling the check out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

v0.13.2

Toggle v0.13.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: render unknown code fence languages as plain text (#169)

Code fences using a language Shiki does not bundle threw at build time,
for example "Language `logql` not found, you may need to load it first".

Shiki's rehype integration only loads a language when it is in the bundle,
and otherwise falls through to `fallbackLanguage`. That option was unset,
so the unknown language reached `codeToHast` and threw. Set it to `text`
so those blocks render unhighlighted instead of failing the page.

Fumadocs' defaults are spread because the config type is not `Partial`;
the highlighter factory merges them anyway, so themes, notation
transformers and meta parsing are unaffected.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

v0.13.1

Toggle v0.13.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: keep every frame when optimizing animated images (#168)

sharp decodes only the first frame unless constructed with
`{ animated: true }`, so animated GIFs were served as still images by both
image pipelines — the /api/image handler (dev and server builds) and the
static build's .webp generation, which MDXImage always prefers via
<picture><source>.

Detect multi-frame sources with a header-only metadata probe, gated on the
two formats that can animate, and pass the flag through to sharp. AVIF
output is skipped for animated input because sharp flattens it; those
requests get animated WebP instead, which every AVIF-capable browser
decodes. The flag is folded into the cache key and ETag only when set, so
still-image keys stay stable while stale single-frame entries are busted.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

v0.13.0

Toggle v0.13.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
chore: bump bun, typescript 6, and update dependencies (#167)

* chore: bump bun to 1.3.14 in CI and typescript to 6.0.3

TypeScript 6 removes the baseUrl option; drop it from tsconfig — the
existing paths mapping is already relative to the tsconfig directory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: update dependencies to latest in-range versions

bun update across the workspace plus two capped bumps: fumadocs-core
16.8.1 → 16.12.1 and @tanstack/react-query 5.100.10 → 5.101.4. Notable:
react/react-dom 19.2.8, react-router 7.18.1, mermaid 11.16, zod 4.4.3,
biome 2.5.5. nitro and vite were already at latest; major bumps
(react-router 8, apsara 1.2, fumadocs-mdx 15, commander 15) deferred.

- Restore @base-ui/react <1.6.0 upper bound that bun update rewrote
- Drop stale nested @codemirror/state@6.5.4 lockfile resolutions so a
  single 6.7.1 instance is installed (duplicates break CodeMirror)
- Widen two test casts for fumadocs-core 16.12 narrowed Folder type

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>