Skip to content

docs: move the Choreo docs into the package READMEs and the gallery realm - #6648

Open
lukemelia wants to merge 2 commits into
mainfrom
cs-13305-move-the-choreo-docs-into-the-package-readmes-and-the
Open

lukemelia wants to merge 2 commits into
mainfrom
cs-13305-move-the-choreo-docs-into-the-package-readmes-and-the

Conversation

@lukemelia

@lukemelia lukemelia commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Background and Goal

CS-13305. The Choreo import (CS-13281) left the source repo's docs/, notes/, README and AGENTS.md in packages/choreo-gallery/ as a holding location. This PR gives each of them a home:

  • Reference material goes to the npm package READMEs.
  • Long-form docs go to the gallery realm, which publishes at choreo.boxel.site.
  • Design history also goes to the gallery realm.

It also points every reference to those files at the new locations. The holding packages/choreo-gallery/docs/ and notes/ directories are gone.

Where to start

  • packages/choreo/README.md is now the main Choreo reference, built from the source README's Choreo half. Its API section was checked against ChoreoContext and <Choreo>'s signature. The source README listed a c.claimed query that doesn't exist (it's c.counterpart) and left out six steps.
  • packages/glimmer-motion/README.md gains "Three rules React does not need" (from the source AGENTS.md), a Testing section and "How it was made". Its helper list no longer names a start helper, which isn't exported.
  • packages/choreo-player/README.md takes in docs/choreo-player.md, except that doc's release checklist.
  • packages/choreo-gallery/realm/docs/ holds the eleven reader docs, plus a README.md index.
  • realm/design/ holds notes/ and the rest of the design history.
  • packages/choreo-gallery/tools/check-realm.mjs (run by pnpm lint:realm) gains checks over the realm docs, the test-app guides and the package READMEs:
    • Relative links and #heading anchors resolve. The heading ids are worked out GitHub-style, the way the host's marked renderer makes them.
    • Every import written in a doc names an export its package has today, following export * re-exports. The imports in design/ aren't checked, because those notes sketch APIs that were never built.
    • Every choreo.boxel.site URL names a file in the realm.
    • Every github.com/cardstack/boxel/{blob,tree}/main/… link names a path that exists.
    • On both kinds of URL, a #fragment names a heading in that file, or in the directory's README.
    • No doc outside design/ links into the archived cardstack/choreo repo.

The rest of the diff is mechanical: moves, and references rewritten to the new paths. That covers about 75 code comments, the choreo skills and the lint configs. In the test-app guides it covers about 130 links that pointed into the archived repo. The API-inventory guide's 351 source links were rebuilt from api-inventory.json, because they still placed Choreo and Film under glimmer-motion/src.

Key decisions and non-obvious mechanics

  • The READMEs use absolute URLs. npm doesn't resolve relative README links reliably. Docs links go to https://choreo.boxel.site/docs/<x>.md, source links to the monorepo on GitHub. The header images come from choreo.boxel.site and won't render until CS-13304 publishes the site. CS-13305 stays open until they're confirmed. Until then, the lint check confirms that each URL names a file in the realm.
  • The design history is published, not archived. Code comments cite notes/*.md for their reasoning, and these links stay resolvable both in the repo and on the site. realm/design/README.md says the notes were written in Choreo's own repo, so the paths they mention are that repo's layout. Historical paths inside the notes are left as they were written.
  • The film-graph syntax sketches are now .gts.md files. They don't compile, and the realm would index a .gts file as a module. Each sketch is fenced verbatim, and .prettierignore keeps prettier from reformatting it.
  • Files that aren't reader docs move to their users:
    • api-inventory.json → choreo-gallery/tools/, as the realm check's input.
    • guide-topics.json → beside the test-app guides it indexes, so the guides port (CS-13298 PR C) takes it along with them.
    • tograph.mjs → choreo-film-app/tools/, the app whose scores it generated. It got a lint pass there; its output is byte-identical for both films.
    • The Lucide licence → realm/licenses/, so it ships with the site.
  • documentation-site.md is kept in realm/design/, not retired. It describes how the test-app guides are registered and tuned, and the guides port (CS-13298 PR C) needs that. source-repo/README.md, source-repo/AGENTS.md and the old gallery deploy doc are retired; the READMEs and the .claude/skills cover their content.

Follow-up

  • CS-13777: the host renders heading ids with a user-content- prefix, and nothing maps a bare #heading link onto them. So in-realm heading links probably don't scroll on choreo.boxel.site. The realm check confirms each fragment names a real heading. Whether the host scrolls to it is the host's job.

@github-actions

github-actions Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Preview deployments

Host Test Results

  1 files    1 suites   20m 47s ⏱️
957 tests 955 ✅ 2 💤 0 ❌
959 runs  957 ✅ 2 💤 0 ❌

Results for commit 18842f5.

Realm Server Test Results

    1 files  ±0    319 suites  ±0   1h 51m 15s ⏱️ + 2m 43s
5 106 tests ±0  5 106 ✅ ±0  0 💤 ±0  0 ❌ ±0 
5 165 runs  ±0  5 165 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 18842f5. ± Comparison against earlier commit 2e2a622.

@lukemelia lukemelia left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] This review checked the package READMEs' API claims against packages/*/src, probed the new check-realm.mjs checks with deliberately broken docs, and swept the tree for references to the old docs/ and notes/ locations. It didn't review the prose of the moved docs beyond what the lint covers.

No blocking issues. The import check is weaker than the gallery README says: it skips every @cardstack/choreo/film import, and fixing that surfaces one import in a design sketch, which needs a call on whether design/ is in scope. The rest are small README corrections.

  1. Follow export * in exportsOf, and decide whether design sketches are import-checked (check-realm.mjs, exportsOf).
  2. glimmer-motion README, Testing: bounds().x and the velocityOf signature.
  3. glimmer-motion README, rule 1: show that the modifier's style takes an object.
  4. choreo README, <Film> example: the required @menuTitle is missing.
  5. Question: do in-realm #heading links land on choreo.boxel.site, given the host's user-content- id prefix? (headingIds)

Follow-up, not this PR: the repo-link and site-URL checks drop the fragment, so cross-README anchors such as packages/glimmer-motion#participant-hosts and #testing aren't validated (both resolve today). headingIds already exists, so a fragment on a blob/main/….md link, or on a tree/main/<dir> link against that directory's README.md, could be checked the same way.

const names = new Set();
for (const node of tree.statements) {
if (ts.isExportDeclaration(node) && !node.exportClause) {
return undefined; // `export *`: anything may come through

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] exportsOf gives up on export *, and @cardstack/choreo/film resolves to src/film.ts, which is a single export * from './film/index.ts'. So none of the 17 @cardstack/choreo/film imports in the docs, guides and READMEs is checked: appending import { NoSuchThing } from '@cardstack/choreo/film' to realm/docs/guide.md still passes pnpm lint:realm. Following the re-export closes it:

    if (ts.isExportDeclaration(node) && !node.exportClause) {
      const inner = exportsOf(
        path.resolve(path.dirname(file), node.moduleSpecifier.text),
      );
      if (!inner) {
        return undefined;
      }
      inner.forEach((name) => names.add(name));
      continue;
    }

With that in place the check flags one import: type ShotArgs in realm/design/film-graph/sylva.gts.md, which no package exports. That file sketches syntax that doesn't exist, and the rest of the design history is kept as written. So the decision is whether the import loop should read design/ at all. I'd drop design/ from the files the import loop reads, and keep it for the link, site-URL and repo-link loops.

Regression in the new check. Not blocking, but the gallery README states this check as a guarantee, so I'd fix it here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] Fixed in 18842f5. exportsOf now follows a relative export *, and returns undefined only for a re-export from another package. It also records export * as ns. The imports in design/ are now skipped, as you suggested, because those notes sketch APIs that were never built. The link, site-URL and repo-link loops still read design/. A planted import { NoSuchThing } from '@cardstack/choreo/film' in guide.md now fails pnpm lint:realm. The header comment and the gallery README both say design/ imports aren't checked.

const withoutFences = (markdown) =>
markdown.replace(/^(```|~~~)[^\n]*\n[\s\S]*?^\1[^\n]*$/gm, '');

/** the ids GitHub (and the host's renderer) give a document's headings */

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] The slugs computed here match the host's, but the host renders heading ids with a user-content- prefix (gfmHeadingId({ prefix: 'user-content-' }) in runtime-common/marked-sync.ts). I can't find anything in host or base that maps a bare #goal fragment onto user-content-goal, the way GitHub's page script does. Do the 44 in-realm #heading links in docs/ and design/ actually land on choreo.boxel.site?

If they don't, that's a follow-up for the host's markdown rendering, not this PR. Either way, this comment should say these are GitHub's slugs and that the host prefixes them. Question; non-blocking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] No, as far as the code shows. The only mention of user-content in host, base and runtime-common is that gfmHeadingId call, and nothing reads location.hash or maps a bare #… link onto the prefixed id. I haven't tried it in a browser. That's now CS-13777, and the PR description lists it as a follow-up. The comment in 18842f5 says these are GitHub's slugs, and that the host renders them with the user-content- prefix.

Comment thread packages/glimmer-motion/README.md Outdated
test('it lands', async function (assert) {
await render(<template><Card /></template>);
await animationsSettled();
assert.strictEqual(bounds(find('.card')!).x, 120);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] bounds() returns { top, left, width, height } (Box in src/test-support/index.ts). So .x doesn't type-check, and at runtime it's undefined, so this assertion can never pass.

Suggested change
assert.strictEqual(bounds(find('.card')!).x, 120);
assert.strictEqual(bounds(find('.card')!).left, 120);

Regression (new in this README); non-blocking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] Fixed in 18842f5: the example asserts .left now.

Comment thread packages/glimmer-motion/README.md Outdated
| `setupMotion(hooks)` | resets what outlives an owner (the projection root, the layout-loop guard, motion speed, and every registered reset) around each test, and fails a test that ran a layout loop |
| `animationsSettled(opts?)` | resolves when every motion element, layout animation and registered busy probe has stopped, and names what was still moving when it times out |
| `bounds(el)` / `shape(el)` / `boundsAndShape(el)` | rects relative to the test root; the cumulative 2×2 transform |
| `velocityOf(el, prop)` | how fast a value is moving right now |

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] velocityOf takes only the element and is async: velocityOf(el): Promise<{ x, y }>, in px/s sampled across two frames. There is no prop argument, so this row should read velocityOf(el), and say that it's async and returns x/y. The row came over from the source README.

Regression (new in this README); non-blocking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] Fixed in 18842f5. The row now reads velocityOf(el) and says it's async and returns { x, y } in px/s, sampled across two frames. Nothing else in the docs, guides or skills uses the two-argument form.

Comment thread packages/glimmer-motion/README.md Outdated
<div class='card' style={{this.accentStyle}} {{motion drag=true}}>

{{! ✓ Motion applies these itself, custom properties included }}
<div class='card' {{motion style=this.accentStyle drag=true}}>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] The modifier's style is a Record<string, unknown> (MotionProps.style in src/node.ts). But the ✗ line binds the same this.accentStyle to the style attribute, which takes a string. Read together, the pair says "move the same value into the modifier", and a string there won't type-check. A distinct name with its shape spelled out would fix it, e.g. {{motion style=this.accentVars drag=true}} with {{! accentVars = { '--accent': … } }}.

Regression (new in this README); non-blocking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] Fixed in 18842f5. The ✗ line says accentStyle is a string, and the ✓ line binds a separate accentVars, with its object shape ({ '--accent': … }) given in the comment above it.

Comment thread packages/choreo/README.md Outdated
```gts
import { Film, IframePicture } from '@cardstack/choreo/film';

<Film @name='sagrada' @seek='exact' @clock={{CLOCK}} @over='everything'>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] @menuTitle is a required argument of <Film> (FilmSignature in src/film/film.gts), and this example leaves it out, so the snippet won't type-check if copied. Add @menuTitle='Sagrada Família', or a … in the tag like the <f.Shot … > line has.

Pre-existing in the source README, now in the published reference; non-blocking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] Fixed in 18842f5: the example passes @menuTitle='Sagrada Família'. The two <Film> examples in realm/docs/film.md already mark the elided arguments with …, so I left them alone.

check-realm follows `export *`, so imports from @cardstack/choreo/film are
checked; skips the design records' imports, which sketch APIs never built;
and checks the heading a choreo.boxel.site or monorepo URL's fragment names.
The glimmer-motion README's testing example, velocityOf row and first rule,
and the choreo README's <Film> example now match the source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tqpxGBVBUCe7nACwttGag
@lukemelia

Copy link
Copy Markdown
Contributor Author

[Claude Code 🤖] The follow-up in the review summary (cross-README fragments) is done in this PR rather than deferred, in 18842f5. The site-URL and repo-link checks now check the #fragment against the target's headings, using a directory's README.md for a tree/ link. They accept GitHub's own #readme anchor. Planted bad fragments on all three kinds of URL fail pnpm lint:realm, and #testing and a #L10 line anchor still pass.

@lukemelia
lukemelia marked this pull request as ready for review October 12, 2026 04:12
@lukemelia
lukemelia requested review from a team and burieberry October 12, 2026 04:12

This branch has not been deployed

No deployments
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