Bifrost documentation
Refresh the gobifrost site by re-capturing screenshots and (optionally) authoring missing pages. Trigger phrases - "refresh docs", "update screenshots", "/bifrost-documentation", "rebuild docs site". Has three modes - bootstrap (one-shot manifest generation, mandatory first run), diff (default - only refresh entries whose Bifrost source changed), full (re-capture and re-author everything).From its SKILL.md
npx -y skills add gobifrost/bifrost --skill bifrost-documentationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
15.0 KB, as published. Nobody here has run it
Bifrost Documentation Pipeline
Refresh gobifrost programmatically: re-capture screenshots, author missing Diátaxis-shaped pages, open a docs PR with a TL;DR.
The docs repo is at ~/GitHub/gobifrost (or clone it from [email protected]:gobifrost/website.git if missing). The bifrost repo's worktree is the source of truth for the running app.
Default behavior — "catch up everything since the last documented commit"
When invoked with no mode, do the whole job in one pass — both existing and net-new surface. The user's expectation: "run it, figure it all out — all new features, all existing features. Know the last commit it fully documented, document all changes and features since, and update it with screenshots." Don't ask which mode; just bring the docs current.
The watermark already lives in the manifest: each entry carries captured_at.bifrost_sha. The lowest sha across all entries is the "fully documented through" commit. Net-new surface is discovered by router-walk ∖ manifest.
Run this sequence (fan out the per-page work with Workflow — one agent per page — when there's more than a handful):
- Establish the watermark.
WATERMARK=$(min captured_at.bifrost_sha across screenshots.yaml entries). Everything ingit log $WATERMARK..HEAD(bifrost) is in scope. - Discover undocumented routes. Enumerate client routes from
client/src/App.tsx(grep -oE 'path="[^"]+"'), normalize, and subtract everyroute:already inscreenshots.yaml. The remainder is net-new surface needing MDX + a manifest entry + fixtures. (Skip non-visual routes:*/callback*,device,mfa-setup,auth/*, param-only redirects.) - Discover stale existing entries. Entries whose
source_globschanged in$WATERMARK..HEAD, or whosecaptured_at.bifrost_shais behind HEAD — these need a re-capture (and a prose check if the feature changed, not just pixels). - Author (fan out). For each net-new route: pick the Diátaxis quadrant, write the MDX page, add the sidebar entry, and add a
screenshots.yamlentry (id,route,seed,diataxis.{page,type}, per-entrymocks/fixtures). For each stale page: update prose if the feature changed. Apply the anti-bloat self-review. MDX comments are{/* */}, never<!-- -->. - Capture in one loop.
scripts/docs/run-pipeline.sh --docs-repo $DOCS --bifrost-repo $BIFROST --full(or--ids <new+stale ids>). Runs headless in the playwright-runner container on this host — no external browser. Pixel-diff gates commits. - Stamp the watermark forward. Every captured entry's
captured_at.bifrost_shais set to the bifrost HEAD it was captured against. That advances the watermark so the next run starts exactly here. - Build + lint + PR.
npm run lint:manifestandnpm run buildmust pass; commit; open the docs PR with the TL;DR.
The named modes below are the surgical primitives this default orchestrates. Reach for a single mode only when you explicitly want just that slice (e.g. lint after hand-editing). The default path is what a release or a monthly catch-up should use.
Modes
| Mode | When | What it does |
|---|---|---|
bootstrap | First run, or after major doc reorganization. Aborts if screenshots.yaml exists unless --force. | Generates screenshots.yaml and bootstrap-report.md from MDX inventory + bifrost router walk. No captures. |
diff (default) | Daily refresh. | Short-lists entries whose source_globs changed since captured_at.bifrost_sha. Captures and pixel-diffs. Commits only PNGs that actually changed. |
full | Major UI shift, theme change, or post-bootstrap first run. | Bypasses the source-glob shortlist. Pixel diff still gates commits, so identical re-renders don't churn git. Also runs the authoring pass for any pages without screenshots. |
lint | After hand-editing the manifest. | Validates screenshots.yaml against schema, MDX cross-references, file existence. No captures. |
Workflow
-
Preflight
- Locate docs repo. Try
~/GitHub/gobifrostthen/tmp/gobifrost. If missing, clone to~/GitHub/. - Verify clean tree (
git status --porcelainempty). If dirty, ask the user to commit/stash before continuing. - Pull
main(git pull --ff-only origin main). - Compare last-update timestamps as a sanity signal:
Print both. IfBIFROST_LAST=$(cd ~/GitHub/bifrost && git log -1 --format=%cI origin/main) DOCS_LAST=$(cd ~/GitHub/gobifrost && git log -1 --format=%cI origin/main) echo "bifrost: $BIFROST_LAST" echo "docs: $DOCS_LAST"BIFROST_LAST > DOCS_LAST, that's normal — diff mode handles it. IfDOCS_LAST > BIFROST_LAST, something is unusual (docs ahead of code); flag it but proceed. - Cut a fresh branch:
docs/screenshot-refresh-YYYY-MM-DD-<short-sha>. - Verify bifrost test stack is up:
./test.sh stack statusin the bifrost worktree. If down,./test.sh stack up. The boot is ~2-5 minutes; warn the user up front. - Set
DOCS_REPO_PATH=<absolute path to docs repo>for downstream tools.
- Locate docs repo. Try
-
Mode dispatch
-
bootstrap:node $BIFROST/scripts/docs/bootstrap-manifest.mjs \ --docs-repo $DOCS_REPO --bifrost-repo $BIFROST $FORCEThen
cd $DOCS_REPO && npm run lint:manifestto confirm schema validity. Commitscreenshots.yaml+bootstrap-report.md. Open PR titled "Bootstrap docs screenshot manifest" with body =bootstrap-report.md. Stop here — do not continue to capture in the same run. -
diff/full:$BIFROST/scripts/docs/run-pipeline.sh \ --docs-repo $DOCS_REPO --bifrost-repo $BIFROST $MODE_FLAGReads JSON output for the TL;DR.
-
lint:cd $DOCS_REPO && npm run lint:manifest. Print result. No PR.
-
-
Authoring pass (full mode only) For each MDX page in
bootstrap-report.md's "Pages without screenshots" list that the user wants documented, generate a Diátaxis-templated stub. Usetemplates/as starting points.- Apply the anti-bloat self-review (below) before committing.
- The skill must NOT silently overwrite hand-written prose; only fills empty/stub pages.
-
Anti-bloat self-review (every prose change) After writing or editing any MDX, sweep for these patterns and cut them:
- "In this section, we'll explore..."
- "It's important to note that..."
- "Let's dive into..."
- "As we mentioned earlier..."
- Any paragraph longer than 80 words in a tutorial or how-to (split, condense, or move to an explanation page).
- Preamble before numbered steps (just start the steps). Reference + explanation pages can be denser. Tutorials and how-tos are terse by Diátaxis discipline.
-
Finalize
- Run
cd $DOCS_REPO && npm run lint:manifestto confirm. - Optionally
npm run buildfor a smoke build. - Stage and commit. Title:
Refresh screenshots and docs (<N> changed, <M> authored). - Push branch.
- Open PR via
gh pr create. Body = TL;DR (template below).
- Run
TL;DR template
## Mode
<bootstrap | diff | full>
## Bifrost SHA range
`<old>..<HEAD>` (or `<HEAD>` if bootstrap)
## Counts
- Candidates short-listed: <N>
- Captures attempted: <N>
- PNGs committed (passed pixel diff): <N>
- Entries unchanged (visual no-op): <N>
- Authored pages (full mode): <N>
## Manual review needed
<list any low-confidence flags from bootstrap-report.md or capture errors>
## Failures
<list any entries that errored, with route + reason>
Diátaxis quadrant rules
When authoring or editing prose, pick the right quadrant and stay in it:
- Tutorial (
getting-started/,*first-*): goal-oriented, a single happy path, no detours. Numbered steps, ≤2 sentences each. - How-to (
how-to-guides/**): one specific task. "How to <verb> <noun>" title. No teaching — assume the reader already understands the concept. - Reference (
sdk-reference/**): describe, don't explain. Tables of params, signatures, return values, one minimal example per item. Reference is the only quadrant where bloat is permitted, and only when needed for disambiguation. - Explanation (
core-concepts/,about/): why-shaped. Cross-link to tutorials/how-tos rather than repeating their content.
If the user asks for a doc and you can't pick a quadrant in one sentence, ask them.
When NOT to use this skill
- The user wants to write a single targeted doc page from scratch — write it directly.
- The user is debugging or fixing a typo — direct edit.
- The bifrost test stack is broken — fix that first; this skill cannot work without it.
- Net-new feature docs ARE in scope — see "Default behavior" at the top. (This used to be a carve-out. It no longer is.) The default no-mode run discovers undocumented routes via router-walk ∖ manifest, authors MDX + manifest entries + fixtures for each (fanning out with
Workflow), then captures in one loop and stamps the watermark forward. The individualdiff/bootstrap/fullmodes remain the surgical primitives, but you do not need to hand-author net-new pages before running — the default path does it. TheAuthoring new capturesprocedure below is the per-page recipe each fan-out agent follows.
Authoring new captures (manual)
When you've written a new MDX page and need a screenshot:
- Identify the route in
client/src/App.tsx. Confirm the route renders empty-state-free with mocked data. - Find the API endpoints the page calls.
grep -nE 'apiClient|useQuery' <component>then look at the route names. For each, write a fixture undergobifrost/fixtures/. - Add a manifest entry to
screenshots.yaml.mocks,actions,crop,callouts,fullPage,settle_msMUST be nested under acapture:key — NOT at the entry top level. The capture spec readsentry.capture.actions/effectiveMocks(entry)fromcapture; top-levelmocks/actionsare silently ignored, which produces a premature screenshot of an empty/loading state that still "passes" (no action ran to fail). Top-level keys are onlyid,image,route,auth_as,seed,external,diataxis,captured_at. Always end actions withwait_for: text="<exact on-page label that only appears once data renders>"so capture can't fire on the empty state. Mock URLs use playwright glob — include BOTH**/api/fooand**/api/foo?**. After capturing, open the PNG and confirm it shows real data, not an empty state — a passing test does not prove the mock matched. - Vite proxy collisions: if the route shares a prefix with a
vite.config.tsproxy rule (e.g./mcp-serverscollides with the/mcprule because of prefix match in dev), usenav_via: { from: "/", click: "<sidebar-link-text>" }so the test stack reaches the page via in-app routing instead of hard navigation. For deeper paths afternav_via, use thegoto_spa: <path>action to push the path via the SPA's history without re-triggering proxy rules. - Capture only the new ids:
scripts/docs/run-pipeline.sh --ids id1,id2 ...so existing entries aren't re-run. organization_idmatters: a few panels (e.g.AgentMCPConnectionsPanel) only render for org-scoped entities. If your fixture hasorganization_id: null, the panel returns null and the capture'swait_forwill time out. Make a separate fixture variant when needed.
Invocation from bifrost-release
The release skill calls this one (in diff mode) before tagging or pushing when bifrost main has moved past the docs repo's last commit. Behavior is identical — there's no special "release" mode. The release flow waits for the docs PR to be opened, then continues with the bifrost tag/push in parallel.
Important: the release skill's step 1b-i identifies net-new feature surface separately and routes around this skill for that case (manual authoring + capture, see above) — diff mode is appropriate ONLY for refreshing existing entries.
Capture gotchas (learned the hard way — read before authoring entries)
mocks/actions/cropMUST nest undercapture:. Top-level ones are silently ignored → premature empty-state screenshot that still "passes." Top-level keys are only:id,image,route,auth_as,seed,external,diataxis,captured_at.- Wait on always-present structure, not data labels. End actions with
wait_foron a card title /h1that renders regardless of data (e.g.text=Maintenance Actions), NOT a fixture-specific row value that may not paint. Add await_mssettle after. - Find the REAL mount endpoints before mocking. Pages use a typed
apiClient.GET("/api/...")— grep the service file, not just the component. Mock every endpoint hit on load (incl./api/organizationsfor org-name badges,/api/maintenance/preflightviauseWorkflows). A missing mock → error card → wait_for times out. Mock the MOST SPECIFIC path first (/foo/barbefore/foo) so a broad glob doesn't shadow it. - Deep routes (
/x/:id) neednav_via+goto_spa, not a hardgoto(which can 404 the SPA shell or hit a Vite proxy prefix). Pattern:nav_via: {from: '/', click: '<Sidebar Label>'}thenactions: [{goto_spa: '/x/<id>'}, {wait_for: ...}]. Seemcp-external-server-detail. - A page gated on
data && objneeds the object-shaped fixture. E.g. SolutionDetail readsdata.solutionfromGET /api/solutions/{id}/entities— the entities fixture must contain{solution:{...}, ...}, and the bare/{id}mock needs its own solution-object fixture (not the entities one). - ALWAYS open the captured PNG and confirm it shows real, correct data — a green test does not prove the mock matched, the right page loaded, or data rendered. This is non-negotiable; it has caught empty states and wrong-page captures every session. See [[feedback_drive_dont_just_test]].
- Capture before build. New MDX
refs breaknpm run builduntil the PNG exists — run the pipeline first, then build. - The pipeline auto-resets test state and auto-installs
scripts/docsdeps, and post-process now runs even if some entries fail (partial captures land; failed ones are reported). Re-run only the failures with--ids.
Hard rules
- Never edit prose without running anti-bloat self-review.
- Never bypass
npm run lint:manifestbefore committing. - Never commit
.tmp-captures/— it's gitignored. - Never run capture mode against a dirty docs tree — abort and ask.
- The skill writes to a branch, never directly to
main. - Never leave a
{/* SCREENSHOT */}placeholder in shipped docs. Either capture it (most surfaces are mockable — even detail pages, via fixtures +nav_via/goto_spa) or fold the description into prose and delete the marker.