← RendemoCONTENT HISTORY

Update to Rendemo

Snapshot Sep 30, 2026 · 23:08 UTC · version 1.0.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "name": "author-a-tour",
  "description": "Use when the user asks to create or change a CODEBASE TOUR inside their own live product, or mentions data-rendemo markers, rendemo.tours.json, rendemo_create_tour, rendemo_add_tour_step, rendemo_list_tours, rendemo_probe_tour_targets, or the tour lockfile. A tour always runs in customer code and requires source markers. A crawled hosted replica is a sandbox demo and must use the sandbox-demos skill instead.",
  "included_files": [],
  "skill_md_contents": "---\r\nname: author-a-tour\r\ndescription: 'Use when the user asks to create or change a CODEBASE TOUR inside their own live product, or mentions data-rendemo markers, rendemo.tours.json, rendemo_create_tour, rendemo_add_tour_step, rendemo_list_tours, rendemo_probe_tour_targets, or the tour lockfile. A tour always runs in customer code and requires source markers. A crawled hosted replica is a sandbox demo and must use the sandbox-demos skill instead.'\nversion: 1.0.0\n---\r\n\r\n# Author a Rendemo product tour\r\n\r\nA **tour** is step-by-step guidance on the user's own live product, which they perform for real.\nIt always uses `data-rendemo` markers in customer source and a `rendemo.tours.json` lockfile.\nRecorded demos and hosted sandbox demos are demos, not tours.\n\r\n**This skill covers two visits.** Building a tour that does not exist yet is sections 1-10. Changing\r\none that does — add a step, reword one, reorder them, drop one, retire the whole thing — is section 0\r\nand \"Editing an existing tour\". `rendemo_list_tours` is what tells you which visit this is, and it is\r\nthe first call either way.\r\n\r\n**This procedure assumes a repo.** If the user gives a URL or existing sandbox instead of customer\nsource, they are asking for a sandbox demo. Load `sandbox-demos` and use the sandbox-demo MCP tools;\ndo not create a tour project as a substitute.\n\r\n## Do not open with a preamble\r\n\r\nSomeone who typed \"build a tour of our app\" has already decided. Opening with an essay on what a tour\r\nis and five things it cannot do spends their attention before they have anything to attach it to, and\r\nit is the single most common way this procedure wastes someone's time.\r\n\r\n**Reading their repo needs no permission.** It is free, reversible and invisible. Go and read it. The\r\nfirst thing in this procedure that is *not* free is writing markers into their source, and that is\r\nwhere the gate belongs — see section 2, where you hand them a table of real elements in their real\r\nproduct that they can actually judge. A step table is disclosure someone can act on. An absent-features\r\nlist is not.\r\n\r\nSo the first reply is: one clause of readiness, one clause of what you are about to do, then work.\r\n\r\n**Write it from this template rather than composing it fresh.** Prohibitions do not survive contact\r\nwith a first turn — \"do not narrate the machinery\" has been stated twice and still produced *\"I'll\r\nstart by checking what tours already exist here\"*, which is a sentence about your own procedure that\r\ntells the user nothing. A shape is harder to drift from than a rule:\r\n\r\n> Signed in to **{workspace}**. Reading the repo for the {intent} sequence…\r\n\r\nand, when the request names an audience, one clause more:\r\n\r\n> Signed in to **{workspace}**. Worth knowing up front: who sees a tour is a conditional render in\r\n> your own code — Rendemo has no rule engine — so \"{their audience}\" becomes your app rendering the\r\n> element only when {condition}. Reading the repo for the {intent} sequence…\r\n\r\nWhat is banned is naming your own apparatus: which skill or file you are in, which step of a\r\nprocedure you have reached, that you are \"checking first\", that you are about to hand off. Say what\r\nyou **found** and what you are **doing**. `rendemo_list_tours` still runs first — it is just not\r\nsomething the user hears about unless it changed the answer.\r\n\r\n## What a tour is — reference, not a script to read aloud\r\n\r\nIt runs in the product through one element:\r\n\r\n```html\r\n<script src=\"https://www.rendemo.com/embed.js\"\r\n        data-demo=\"<workspace>/<tour-slug>\" data-mode=\"tour\" async></script>\r\n```\r\n\r\nThat resolves each step's `data-rendemo` marker on the live DOM, draws the same step card the published\r\ndemo draws, advances when the viewer does the real thing (`data-rendemo-do`), waits for a target that\r\nhas not appeared yet, skips steps whose `route` is not the current page, and resumes on the right step\r\nacross navigations and reloads.\r\n\r\n**The five limits, and where each one belongs.** Every one is real and a developer rolling this out to\r\nreal users needs all of them — but not in one block, and not before they have seen the tour. Each\r\nbecomes a sentence that changes a decision they are actually making, at a specific moment:\r\n\r\n| Limit | Say it at |\r\n| --- | --- |\r\n| No targeting rule engine — conditional rendering is the primitive; `routes` filters pages, not people | §10 — **unless the request names an audience, and then the first reply** (see below) |\r\n| Progress is `localStorage` — `user` scopes it per browser, never syncs across devices | §10, with the `user` attribute |\r\n| Analytics observe Do-Not-Track and nothing else — no consent API | §7, before publishing |\r\n| Branches are viewer-chosen, never rule-evaluated | §5, and only if they want a fork |\r\n| Kill switch stops it being *served* in 30s; it does not reach open tabs | \"Retiring a tour\", or whenever they ask how to stop it |\r\n\r\nMost of these are already written into those sections. Reaching them early buys nothing: the user\r\ncannot evaluate \"no rule engine\" before they have seen a step table, and by the time it matters they\r\nwill have forgotten you said it.\r\n\r\n**The one exception, and it is not rare — it is most onboarding tours.** Deferring the targeting\r\nlimit is right for \"show me around the app\" and wrong for \"show this to users who don't have a\r\nrecording yet\". When the request *names an audience* — anything of the shape \"users who…\", \"trial\r\naccounts\", \"admins\", \"first-time visitors\", \"people who haven't done X\" — the limit is not a\r\nfootnote about the element, it is a fact about **the thing they just asked for**, and disclosing it\r\nat §10 means they learn at the end that the headline requirement was never Rendemo's to satisfy.\r\n\r\nSo: if the request names an audience, the first reply says, in one clause, that **who sees a tour is\r\na conditional render in their own code** — Rendemo has no rule engine and cannot know their users —\r\nand then names the specific condition you will be asking them to write, e.g. \"so this becomes: your\r\napp renders the element only when the recording count is zero.\" Then keep going and scan the repo.\r\nIt costs one sentence and it is the difference between a constraint and a surprise.\r\n\r\nTwo things follow from it that must not be improvised:\r\n\r\n- **That conditional is host code, and it is nobody's to check.** `rendemo check` does not see it,\r\n  the lockfile does not describe it, the kill switch does not reach it, and if it regresses the tour\r\n  shows to everyone or to no one with nothing reporting it. Say that once, when you hand the gate\r\n  over — not as a disclaimer, as the reason it belongs in their code review.\r\n- **Do not write it silently.** If the gate needs a component, propose it in the step table alongside\r\n  the markers so it is approved as part of the same change, not slipped in.\r\n\r\nThe detail behind each, for when its moment arrives:\r\n\r\n1. **Targeting: a primitive plus two declarative cases, and no rule engine.**\r\n   The primitive is yours and it is the honest primary answer: **a developer conditionally rendering\r\n   the element is targeting.** If only trial admins should see it, render it only for them — Rendemo\r\n   cannot know your roles and should not try. On top of that, two attributes:\r\n   `routes=\"/app,/app/projects\"` limits which pages a tour may run on at all (re-evaluated on every\r\n   navigation, so it starts when the visitor arrives on one) and `when=\"always\"` replays a tour the\r\n   viewer already finished, for a \"Take the tour\" button. **Still absent:** any rule engine — no \"new\r\n   users only\", no per-plan, per-role or feature-flag condition, no percentage rollout. Do not describe\r\n   `routes` as segmentation; it is a page filter.\r\n2. **User identity: opt-in, and it does not sync.**\r\n   `data-user=\"u_123\"` on the tour's script tag keys progress by that id and sends\r\n   it with the analytics events, so two people sharing a browser no longer share one position and\r\n   completion is a fact about a person. The id is **whatever opaque string you choose** — Rendemo never\r\n   resolves it to anybody and collects nothing else about them. Absent, everything behaves as it always\r\n   did, which is what an anonymous marketing page needs. **Still absent:** progress is still stored in\r\n   `localStorage`, so `user` makes it per person *within a browser* — it is not a server-side profile,\r\n   and **switching device or clearing storage still restarts the tour.**\r\n3. **Analytics: it reports to the same place a published demo does.**\r\n   The tour beacons `demo_loaded`, `step_shown`, `step_completed`, `step_dwell` and `demo_completed` to\r\n   Rendemo's existing demo-analytics ingest, over `sendBeacon`, so a step completion is not lost to the\r\n   navigation the step itself caused. Completion rate, per-step drop-off, dwell and abandons all appear\r\n   in that demo's own analytics — no new dashboard to find. A dismissal is recorded as the step's dwell\r\n   reason, and a give-up as `unresolved` with **no** completion. The DOM events (`rendemo:ready` /\r\n   `:step` / `:complete` / `:close`) are unchanged and still the host's hook. **Still absent:** the only\r\n   privacy posture observed is **Do-Not-Track** (which is all the replay player observes either — there\r\n   is no consent API and no cookie banner in this path), and nothing is sent at all when the payload\r\n   has no project to attribute it to. Say this plainly to anyone with a consent regime to satisfy.\r\n4. **Branching: the plan's own choices, honoured.**\r\n   A step can offer up to three author-declared paths (`choices`, set with `rendemo_update_step`), each\r\n   jumping to another step id — forwards, or backwards for a \"show me that again\". Afterwards the tour\r\n   continues linearly from wherever the branch landed, and progress resumes on the chosen path.\r\n   **Still absent:** branches are **viewer-chosen, never rule-evaluated** — there is no \"if they are on\r\n   the Pro plan, go to step 5\". And a step whose target never appears still gives up **visibly** after a\r\n   bounded wait (a card saying so, with \"Skip this step\" and Close) and shows **no** branch buttons:\r\n   branching is author-declared paths, not error recovery.\r\n5. **The kill switch: per demo, per workspace, and 30 seconds.**\r\n   `rendemo_take_demo_offline` (or **Take offline** in the library's ⋯ menu) stops one tour being\r\n   served; **Settings → Product tours** turns off every tour in the workspace at once, which is what\r\n   you reach for when a release has moved the UI out from under every tour's markers. Neither deletes\r\n   anything — turning it back on serves the identical artifact. The payload is cached `s-maxage=30`, so\r\n   propagation is bounded at **30 seconds** rather than the five minutes (plus an hour-long stale\r\n   window) it used to be. **Still absent:** it is a \"stop serving it\" switch, not a \"reach into open\r\n   tabs\" one — a visitor whose tour is already running finishes it. Removing the element still works\r\n   too.\r\n\r\nAlso true: a tour is **not visible in the studio** (the studio needs a capture; a tour has none), and no\r\nreplay artifact is built for it — no HTML, no poster, no export, no localization. Say this when they\r\nask where to look at it; the answer is the **preview link** in step 6.\r\n\r\n**The one thing worth raising before the repo scan** — because it is the only one that can make this\r\nwhole procedure the wrong answer for them — is that a tour writes `data-rendemo` attributes into their\r\nsource and **those must ship to production**. Someone who cannot change the product's code cannot have\r\na tour, and should hear that in a clause now rather than after you have proposed nine steps. If they\r\ncan change their code, this needs no acknowledgement: say it and keep going.\r\n\r\nIf they want something a visitor **watches** rather than performs for real — a marketing page, a sales\r\nfollow-up — that is a published replay demo installed with `<rendemo-demo>`, a different skill\r\n(`embed-a-demo`).\r\n\r\n## 0. Is this a first visit or a second one?\r\n\r\n**Call `rendemo_list_tours` first, always.** It returns every tour in the workspace with its\r\n`projectId`, slug, step count and whether it is live — replay demos excluded, because they are a\r\ndifferent artifact and a marker step cannot be added to a recording.\r\n\r\n**Do not narrate this call.** \"First, let me check whether a matching tour already exists\" is a\r\nsentence about your own procedure; the user learns nothing from it. Make the call, then say what it\r\nfound only if it changes what happens next — an existing tour they might have meant is worth a\r\nsentence, an empty workspace is not.\r\n\r\nIf the intent is about a tour that already exists, **skip everything below and go to \"Editing an\r\nexisting tour\"**. Name only the constraints their specific change touches.\r\n\r\nIf nothing matches, this is a first visit: continue from step 1.\r\n\r\n## Editing an existing tour\r\n\r\nFour changes, and one of them has a trap.\r\n\r\n**Reword a step** — `rendemo_update_step({ projectId, stepId, title?, blurb?, … })`. `rendemo_get_plan`\r\ngives you the step ids. The tour renders `title`, `blurb`, `eyebrow`, `advanceLabel`,\r\n`successMessage`, `recoveryHint`, `emphasis`, `emphasisColor` and `choices` and nothing else; the tool\r\nnames anything it dropped.\r\n\r\n**Add a step** — `rendemo_add_tour_step` appends, so a step that belongs in the middle is *append then\r\nreorder*. It returns the exact attribute to write; write it into source as a reviewable diff, the same\r\nas the first time.\r\n\r\n**Reorder** — `rendemo_reorder_steps({ projectId, order })`. Pass the **complete** ordered list of step\r\nids; a partial list drops the missing ones from the story order. Reordering touches no source at all —\r\nthe markers say *where*, the order says *when*.\r\n\r\n**Remove a step — the one with the trap.** `rendemo_remove_tour_step({ projectId, stepId })` deletes it\r\nfrom the plan and returns `markerToStrip` / `attributeToStrip`. **The marker does not remove itself.**\r\nLeft in source it becomes an `orphan-marker` and `rendemo check` fails the build for whoever pushes\r\nnext — the safety feature turning into a nuisance exactly when someone is backing out of something.\r\nSo:\r\n\r\n1. Call the tool.\r\n2. In the **same diff**, delete `data-rendemo=\"<tour>/<step>\"` from the element, along with any\r\n   `data-rendemo-do` or `data-rendemo-wait` on it. If the element only existed to be marked — a\r\n   wrapper someone added for the tour — say so and let the user decide whether the element goes too.\r\n3. Show the user that one diff: the step gone, the attribute gone.\r\n\r\nThe tool refuses two cases rather than producing a broken tour, and both need a decision from you, not\r\na retry:\r\n\r\n- **Another step's branch choice points at this one.** A dangling `goToStepId` is dropped silently by\r\n  the payload builder, so the button would just vanish for the viewer. Edit those choices with\r\n  `rendemo_update_step` first — the message names which steps.\r\n- **It is the last step.** A tour with no steps cannot be previewed or published. Retiring the whole\r\n  tour is a different, deliberate act — `rendemo_remove_tour`, see \"Retiring a tour\" below.\r\n\r\n**After ANY of the four, in this order and without skipping:**\r\n\r\nBefore preview or publish, call `rendemo_review_step_presentations` and resolve every target,\r\nrole, required-content, copy-fit, and responsive finding. This review is a publishing contract,\r\nnot optional polish. A first or last action remains an action; never choose Scene Break or\r\nOutcome Stage merely because of sequence position.\r\n\r\n1. **Preview** — `rendemo_get_tour_preview`. Same 30-minute bearer link, same rule that the markers\r\n   must exist in the build you point it at. A removal is the case where this matters most: it is the\r\n   only way to see that the tour still reads as a sequence with the step gone.\r\n2. **Publish again** — `rendemo_publish_demo`, after asking. **Until you do, the live tour is the old\r\n   one.** Editing the plan changes nothing a visitor sees.\r\n3. **Regenerate the lockfile** — `rendemo_get_tour_lockfile({ projectId, lockfile })`, passing the\r\n   current `rendemo.tours.json` so other tours survive. Until you do, the committed lockfile still\r\n   describes the steps you changed, and `check` verifies the old shape.\r\n4. **Run the check** — `npx rendemo check` — and report its real exit code.\r\n\r\nSkipping 3 after a removal is the specific way to leave CI failing on a marker that is already gone.\r\n\r\n## 1. Find the sequence in the code\r\n\r\nRead the repo, do not interview the user. You are looking for the ordered sequence of real elements a\r\nnew user touches: the entry route, the primary action on it, the route it navigates to, and so on.\r\n\r\nUseful evidence, roughly in order of confidence: route files and their paths, `data-testid`\r\nattributes, form submit handlers, primary/CTA button components, existing onboarding or empty-state\r\ncopy, and `href`s between the pages.\r\n\r\n**In a large repo, delegate the scan to a subagent** and ask it to return only the candidate step list\r\nwith `file:line` for each target element. A full-repo read floods the main context and you need that\r\ncontext for the approval conversation.\r\n\r\n## 2. Propose the steps and get approval — mandatory\r\n\r\nPresent a table before creating anything:\r\n\r\n| # | step slug | route | target element (`file:line`) | `do` | what the card says |\r\n|---|---|---|---|---|---|\r\n\r\nRules for the proposal:\r\n\r\n- Step slugs and the tour slug are lowercase letters, digits and hyphens, no leading or trailing\r\n  hyphen. An invalid slug is rejected by the tools, not silently fixed.\r\n- `do` is `click`, `type`, or `hover`, and only when that action is what completes the step. Omit it\r\n  and the step waits for an explicit Next. A tour where every step is Continue is a slideshow; aim for\r\n  at least half of them being something the user really does.\r\n- Every target must be an element **that exists in source now**, with the line you found it at. If\r\n  you could not confidently locate a target, say so and leave it out — a marker on a\r\n  nearly-right element points at the wrong thing forever, and an unplaced step is the safer failure.\r\n- **Ask two questions of every target before you propose it**, because both have runtime-only answers\r\n  that `check` cannot give you:\r\n  1. *How many of these are in the DOM at once?* One line of source inside a `.map()` is N elements at\r\n     runtime, and the tour treats an ambiguous marker as a **missing** target. That step needs `match`\r\n     (`\"first\"`, `\"last\"`, or a 1-based integer).\r\n\r\n     **The precedence, because it decides the commonest case and reads backwards if you guess:\r\n     resolution filters to the elements the visitor can SEE, and only then applies `match`.** So\r\n     `match` disambiguates among visible elements — a hidden duplicate never shifts what `\"last\"` or\r\n     `nth: 2` means, and a copy in a closed drawer does not make an unmatched marker ambiguous.\r\n\r\n     Which settles the **responsive** case, and settles it the opposite way to intuition: a control\r\n     rendered twice for two breakpoints — a `hidden md:flex` sidebar and a phone nav — **can and\r\n     should carry the same marker on both.** One is on screen at a time, so the viewport does the\r\n     disambiguating. Give that step `match: \"visible\"`: at runtime it behaves exactly as no `match`\r\n     does (which is already correct), and it is what tells the offline `check` that two occurrences\r\n     in source are deliberate rather than a mistake. Do **not** reach for `\"first\"` here — it passes\r\n     the check too, but it says source order decides when the viewport does, and it silently pins the\r\n     step to whichever branch the bundler happened to emit first.\r\n\r\n     Never conclude that a tour must be desktop-only because a nav is duplicated. That is a solved\r\n     shape, and dropping the nav steps costs the user a tour on every phone for no reason.\r\n  2. *Is it visible when the step is reached?* Resolution **prefers a visible match**: among several\r\n     elements carrying the marker it picks the one on screen, and if the only match is invisible it\r\n     treats the step as not-yet-present and keeps waiting, then gives up visibly on timeout. So a tab\r\n     panel toggled with a `hidden` class rather than unmounted no longer anchors the card to a zero\r\n     rect in the corner of the viewport — but it does mean the step shows nothing until the visitor\r\n     opens that tab. Still give it a `recoveryHint` naming how to get there. A `wait` is now optional\r\n     rather than the fix, and is worth adding only when the real precondition is something visibility\r\n     cannot express (a fetch settling, a form becoming valid). Note the visibility test reads whether\r\n     the element renders a box at all, not how big it is, so a legitimately 0x0 icon button resolves.\r\n- **Prefer a target you cannot mark cleanly over restructuring their UI.** If the natural anchor is\r\n  produced by a shared component that does not forward props, either mark a different element or add a\r\n  narrow attribute-only pass-through to that component (Rendemo's own studio needed one for its\r\n  toolbar popovers: a single `marker` prop spread onto the trigger button, not a `...rest`). Never wrap\r\n  the element in a new `<div>` to hang the attribute on — that injects a layout box into their CSS,\r\n  which is the whole reason `demo()` is an attribute spread and not a component.\r\n\r\n### Ask the open decisions ONE AT A TIME. \"Go\" is not an answer to four questions.\r\n\r\nA step table almost always surfaces decisions the scan cannot settle: a nav that only exists on\r\ndesktop, a shared component that needs a prop to carry a marker, a CI assertion that will go red, a\r\ngate component to restore. It is tempting to write those up as prose and end with \"say go\" — and what\r\ncomes back is `go`, which answers none of them. You then pick defaults for all four, and the user has\r\nmade a decision they did not know they were making. That is the single most common way this procedure\r\nships something the user would have chosen differently.\r\n\r\nSo:\r\n\r\n- **The step table gets one approval.** That is the gate on writing markers, and a yes/no fits it.\r\n- **Every open decision is asked as its own question, with the options named.** If the harness offers\r\n  a structured choice, use it — one round trip, four answers. If it does not, number them and ask for\r\n  numbered answers.\r\n- **Never bundle a configuration question into the approval.** \"Say go — and where does your app\r\n  run?\" invites a one-word reply that loses the second half.\r\n- **If a decision comes back unanswered, say which default you took and why, in the same breath as\r\n  the work.** A default chosen aloud can be corrected; a default chosen silently cannot.\r\n\r\n### Find out where the app runs — and that it ANSWERS — before you write a marker\r\n\r\n`baseUrl` is asked for at preview time, which is far too late to discover that nothing is serving it.\r\nThe preview is the first moment anything in this procedure needs a running app, and by then you have\r\nwritten attributes into their source, created a tour, and started a 30-minute clock.\r\n\r\nSo ask where their app runs as part of the same round trip as the step table, and **confirm something\r\nanswers there** before writing markers — a single request is enough. If nothing does, say so and let\r\nthem start it. Getting a dev server up can be its own small ordeal (an empty `node_modules` in a\r\nworktree, a build that needs a real install), and it is much cheaper to hit that before the source\r\nedits than between the markers and the preview.\r\n\r\nIf they cannot run the app anywhere yet, that is fine — say plainly that the tour will be authored\r\nblind and cannot be previewed until it runs, and let them decide whether to continue.\r\n\r\n### If you are about to restore something that was deliberately deleted, ask first\r\n\r\nMarkers, pass-through props and mount components are sometimes *removed on purpose* — a cleanup, a\r\nrollback, a decision the user made last week and has not forgotten. Reading them back out of git and\r\nre-creating them is not a neutral act, and \"it was deleted in b503c91\" is a fact you already have\r\nfrom the scan.\r\n\r\nBefore restoring anything the history shows was deliberately removed, name the commit and ask whether\r\nit should come back. One sentence. If they say yes it costs nothing; if they say no you have avoided\r\nquietly reverting their own decision.\r\n\r\nThen stop and wait. **Writing markers modifies the customer's source**, and the tour slug you agree on\r\nis baked into every one of those attributes — renaming it later orphans all of them.\r\n\r\n## 3. Create the tour and its steps\r\n\r\n- `rendemo_create_tour({ name, tourSlug })` → `projectId`. Every marker for this tour starts\r\n  `<tourSlug>/`.\r\n- `rendemo_add_tour_step({ projectId, step, route, do?, wait?, match?, title?, blurb? })` once per\r\n  step, in order. Each call returns the **exact attribute string** to place. Use what it returns; do\r\n  not compose the attribute yourself.\r\n- `match` is flat on the wire: `\"first\"`, `\"last\"`, or a positive integer meaning the 1-based nth\r\n  element. Pass it for every target you answered \"more than one\" to in step 2 — it is the only way to\r\n  express that, and it cannot be added later by editing `rendemo.tours.json`.\r\n- Two steps cannot share a marker; the tool refuses the duplicate.\r\n\r\n## 4. Write the markers into source\r\n\r\nPut the returned attribute on the element the step points at:\r\n\r\n```jsx\r\n<button data-rd=\"onboarding/new-project\" data-rd-do=\"click\">New project</button>\r\n```\r\n\r\n- **`data-rd` and `data-rendemo` are the same attribute** (likewise `data-rd-do` / `data-rendemo-do`\r\n  and `data-rd-wait` / `data-rendemo-wait`). Both resolve identically in the scanner, the runtime and\r\n  the lockfile, and a page may mix them. `rendemo_add_tour_step` returns the short form as\r\n  `attribute` and the long one as `attributeLong` — **match whatever the repo already uses**, and\r\n  prefer the short form only in a repo with no markers yet. Consistency inside one codebase beats\r\n  brevity.\r\n- `data-rd-do` is optional (what completes the step). `data-rd-wait=\"<selector>\"` is optional (a\r\n  selector that must exist before the step is reachable). **Neither is read at runtime** — the\r\n  overlay takes `do` and `wait` from the published plan, and these attributes state the same fact on\r\n  the element so a reader does not have to open the studio. Editing them by hand changes nothing;\r\n  change the step and republish.\r\n- **Markers must ship to production.** They are what the tour anchors to at runtime, on every\r\n  visitor's page. Do not strip them in a production build, and do not put them behind a dev-only flag.\r\n- Rendemo's own repo has a zero-runtime helper, `demo(id, opts?)` in `lib/flow/marker.ts`, that\r\n  spreads the same attributes: `<button {...demo(\"onboarding/new-project\", { do: \"click\" })}>`. It\r\n  exists **only in a Rendemo checkout or a repo that vendored that module.** In any other repo, write\r\n  the plain attributes — do not import a module that is not there.\r\n- Present the marker edits as a reviewable diff and let the user read it before you continue.\r\n\r\n## 5. Author the copy\r\n\r\nTour steps use `rendemo_update_step` for title, explanation, emphasis, and choices. Presentation is\r\nauthored through the same registry-backed workflow used by Studio:\r\n\r\n1. Call `rendemo_list_presentation_recipes`.\r\n2. Call `rendemo_suggest_step_presentations` for a varied sequence based on each step's job.\r\n3. Present the proposed sequence for approval.\r\n4. Apply it with `rendemo_apply_presentation_direction`; use `rendemo_set_step_presentation` only for\r\n   a focused correction. That tool also owns bounded per-step typography, `textStyles` for each\r\n   visible text block, and semantic `border` controls (treatment, motion, width, radius, two colors,\r\n   speed, and direction). Use animated borders to communicate direction, progress, or completion;\r\n   do not add them to every step.\r\n   Text styles support font family, 10-96px size, weight, alignment, color, italic, and underline. These\r\n   are the same semantic fields written by Studio's direct on-card text editor.\r\n5. Set demo-wide material, density, accent behavior, and motion intensity with\r\n   `rendemo_set_demo_presentation_theme`. The card-to-target `targetBeam` defaults to false; enable\r\n   it only when a connector materially improves target clarity.\r\n6. When the user approves one card's visual styling and wants consistency, call\r\n   `rendemo_apply_step_presentation_style_to_all` with that step as the source. This copies visual\r\n   modules only; do not replace the demo's recipe sequence or content.\r\n7. Run `rendemo_review_step_presentations` and open its signed real-render review URL before publish.\r\n\r\nRecipes are compositions, not skins. Beacon, Magnifier, Margin Note, Action Dock, Flowline,\r\nSpotlight, Control Room, Decision Canvas, Proof Stack, Journey Map, Scene Break, and Outcome Stage\r\nhave distinct anatomy, responsive modes, copy budgets, motion, and target relationships. Do not\r\nflatten a tour into one repeated recipe when the story changes jobs.\r\n\r\nThe compatibility matrix is a hard contract. Never assign a recipe to an unsupported step role or\r\ninvent a material, motion signature, target\r\nrelationship, content block, arbitrary CSS rule, percentage size, or free position. If MCP rejects a\r\ncombination, choose a supported combination from the recipe manifest instead of working around it.\r\nTarget-aware recipes must keep the measured target clear; full-stage narrative recipes belong on\r\ntransition or outcome steps, not click steps.\r\n\r\n`choices` is worth authoring on a tour: each is a button on the card that jumps to another step id, so\r\n\"are you setting this up for yourself or for a team?\" is a real fork rather than a paragraph asking the\r\nviewer to skip ahead themselves. Keep it to genuinely different paths — the tool refuses a choice\r\npointing at a step that does not exist, and the payload silently drops one pointing at a step the tour\r\nis not serving.\r\n\r\n**Say this the first time they want a fork, and only then:** branches are **viewer-chosen, never\r\nrule-evaluated**. There is no \"if they are on the Pro plan, go to step 5\" — the viewer picks by\r\nclicking. And a step whose target never appears gives up **visibly** rather than taking a branch;\r\nbranching is author-declared paths, not error recovery.\r\n\r\nWhat changes a tour's presentation: its recipe, permitted modules, demo presentation theme, and\r\nper-step emphasis. Border motion must communicate direction, progress, target handoff, or completion;\r\nsteady states stay calm and reduced motion removes travel and pulsing without collapsing hierarchy.\r\n\r\nCopy is Rendemo's, not the repo's: source declares *where and in what order*, Rendemo owns *what it\r\nsays*. Do not write card text into the source files.\r\n\r\nWrite copy that teaches. A title that names the control (\"Click a clip to open its step\") and a blurb\r\nthat says why it matters beats a label. `successMessage` is what the viewer sees when they get it\r\nright; `recoveryHint` is the only thing they get when the step gives up, so it must name where the\r\ncontrol actually is.\r\n\r\n## 5b. Probe the targets — before the preview link exists\r\n\r\n```\r\nrendemo_probe_tour_targets({ projectId, baseUrl })\r\n```\r\n\r\nIt fetches each step's route from the running app and reports, per step, whether the tour would find\r\nits marker there. Run it **before** minting a preview link, every time. It costs seconds, spends none\r\nof the link's thirty minutes, and it catches the three failures that otherwise consume a person's\r\nreview:\r\n\r\n- markers not deployed to the host you are about to point them at,\r\n- a route that renders a sign-in stub, so the step's element is not there at all,\r\n- a marker resolving to several elements with no `match`, which never anchors.\r\n\r\n**Read `absent` correctly, and say it correctly.** The probe sees the HTML the server sends a\r\nsigned-out stranger. A marker rendered after sign-in, or only on the client after hydration, is\r\ngenuinely missing from that response and genuinely present for the real visitor. `absent` therefore\r\nmeans *look here*, not *broken*.\r\n\r\nThat distinction is the whole value, so pass it on rather than swallowing it: when you hand over the\r\npreview link, **name the steps the reviewer must be signed in to see.** A reviewer who walks ten\r\nsteps and finds six reporting a missing target, with no warning, reports six bugs — and every one of\r\nthem costs a round trip to explain away. Told first, they sign in and review ten steps once.\r\n\r\nIf a step is `absent` for a reason that is *not* auth or hydration, fix it before previewing. A\r\npreview is for judging copy and anchoring; it is not the place to discover the markers are not\r\ndeployed.\r\n\r\n## 6. Preview the draft — before you offer to publish\r\n\r\n**This step comes before publishing, and that ordering is the point.** Publishing used to be the only\r\nway to see a tour, which meant making it live to find out whether it was right. Preview is also the\r\nonly thing that can catch bad copy: the lockfile carries no card text, so no offline check can tell you\r\nthat a tour's cards say nothing useful.\r\n\r\n```\r\nrendemo_get_tour_preview({ projectId, baseUrl })\r\n```\r\n\r\nIt returns a URL like `http://localhost:3000/projects?rendemo_preview=<token>` — the route the tour's\r\n**first** step expects, on the host you named, with a signed token in the query string. Opening it runs\r\nthe **current draft**: nothing is published, no analytics are recorded, progress is kept out of a real\r\nvisitor's storage, and every card carries a persistent **\"Preview — draft, not live\"** badge so a draft\r\nis never mistaken for the live thing.\r\n\r\n- **Ask where their app runs.** `baseUrl` defaults to `http://localhost:3000`; a link pointed at the\r\n  wrong host looks exactly like a broken tour. A dev server, a staging deploy and production are all\r\n  valid targets.\r\n- **The link is a bearer token and expires 30 minutes after it is ISSUED — not after it is first\r\n  opened.** Anyone holding it sees the draft, with no sign-in. Two consequences, and the first is the\r\n  one that actually bites: **mint it at the moment the reviewer is ready to look.** Issuing it and\r\n  then running an install, a build, or a deploy spends the window on work the reviewer never sees, and\r\n  they get a link that dies mid-review. Do the probe, get the app running, get yourself to the point\r\n  where the only thing left is a person looking — *then* call this. Second: say the bearer property\r\n  when you hand it over, and re-issue rather than trying to extend one. Re-issuing is free and\r\n  instant; if it lapses while they are reviewing, just mint another.\r\n- **A tour that has never been published previews fine.** That is the case preview matters most for —\r\n  you cannot inspect the first version of a tour by publishing it.\r\n- The tool refuses if no step has a route yet: there would be no page to open. Add the steps first.\r\n- The one gate a preview token does **not** lift is the workspace kill switch. If **Settings → Product\r\n  tours** is off, the preview 404s — an operator who turned tours off was not saying \"except drafts\".\r\n\r\n**THE HONEST LIMIT — say it before you hand the link over.** A preview sends the draft **plan** to a\r\nbrowser. It cannot send the draft **markup**, which lives in the user's application and not in\r\nRendemo. So the `data-rendemo` markers have to already exist in whatever build is answering at\r\n`baseUrl`:\r\n\r\n- **Dev server:** immediate. Save the file, reload, the step anchors.\r\n- **Staging or production:** the commit that adds the markers has to be **deployed there first**.\r\n  Preview against a build that predates the markers and every step correctly reports a target it\r\n  cannot find — a real failure with a cause that has nothing to do with the tour.\r\n\r\nThe usual order therefore is: write the markers, preview against the dev server, deploy the markers\r\nwith their normal release, then publish.\r\n\r\nThe same applies to the element itself. `embed.js` reads the preview token off the page URL and acts on\r\nit only where a `<rendemo-demo … mode=\"tour\">` element is actually mounted, so the two lines from\r\nstep 10 must be in the build being previewed too. Putting them in the dev build is free; **do not ship\r\nthat element to production before the tour is published** — an element pointing at an unpublished tour\r\nrenders a visible \"This product tour could not be loaded.\" note to every real visitor of those routes.\r\n\r\nHand over the link, say what to look for (the copy, where each card anchors, whether the `do` steps\r\nadvance when they really click), and **wait**. Fix what they report — `rendemo_update_step` and the card\r\ntools take effect on the next load of the same link, because the preview serves the draft itself rather\r\nthan a copy of it, and it is never CDN-cached. Only when they say the tour is right do you move on.\r\n\r\n## 7. Publish — mandatory checkpoint\r\n\r\nAsk before calling `rendemo_publish_demo({ projectId })`. Publishing claims the tour's slug\r\n**workspace-wide** and writes the published plan and its hash. It is the point after which the markers\r\nin the repo and the published tour are contractually tied together — and it is the **last** authoring\r\nstep, not the way to see your work.\r\n\r\n**This is where the analytics fact belongs, in the sentence that asks.** A live tour beacons\r\n`demo_loaded`, `step_shown`, `step_completed`, `step_dwell` and `demo_completed` into that demo's\r\nexisting analytics — and the only privacy posture on that path is **Do-Not-Track**. There is no consent\r\nAPI and no cookie banner here. Anyone with a consent regime to satisfy needs that before they say yes,\r\nnot after; it is one clause, and this is the moment it is actionable.\r\n\r\nThe publish is validated, and a refusal says what to do. Two of them still need a decision from you\r\nrather than a retry:\r\n\r\n- **A validation refusal (422)** lists **each bad step and why** — a missing or malformed target, a\r\n  marker naming a different tour, two steps sharing one. Fix exactly the steps it names, then publish\r\n  again.\r\n- `slug_taken` — another artifact in this workspace already holds that slug. Publish is **refused\r\n  rather than renamed**, because renaming would orphan every marker already committed. Pick a\r\n  different `tourSlug` — which means going back to step 2, since every marker in source changes too.\r\n- `approval_required` — this workspace gates publishes behind review.\r\n- **A locale-pinned tour publish is refused (400).** Never pass a `locale` when publishing a tour.\r\n\r\nA successful tour publish reports the demo id, slug, step count and plan hash — and **no URL**, because\r\nthere is nothing to open. Carry the plan hash to step 8; there is no link to give the user.\r\n\r\n## 8. Write `rendemo.tours.json`\r\n\r\n- **Read the repo root's existing lockfile first** — `rendemo.tours.json`, or the older\r\n  `rendemo.flow.json` if that is what the repo still has. If one exists, pass its full text as\r\n  `lockfile` to `rendemo_get_tour_lockfile({ projectId, lockfile })`. **One file describes every tour\r\n  in the repo** — writing a single-tour file silently stops checking the others' markers. The tool\r\n  refuses an unparseable input rather than replacing it, and reports which other tours it preserved.\r\n- Write the returned `contents` to `rendemo.tours.json` at the repo root and commit it. A repo still on\r\n  the old filename should be moved to the new one; `rendemo check` reads either, preferring the new.\r\n- **Never hand-edit this file.** It describes the *published* tour, which is what lets the check run\r\n  offline with no token.\r\n\r\n## 9. Verify\r\n\r\nThe check scans source for markers and confirms every lockfile step resolves to exactly one — offline,\r\nno auth, source-only. Exit `0` pass, `1` step failures, `2` bad or missing lockfile.\r\n\r\nRun `npx rendemo check` in the repo you're working in. That is the normal, correct way to run it — the\r\npackage is published on npm as `rendemo`, currently `0.2.0`, and this needs no install. **`0.2.0` or\r\nnewer is required**, because that is the version that reads the `rendemo.tours.json` name.\r\n\r\n**General rule: if the repo you are in has its own package named `rendemo`, `npx` will resolve that\r\nlocal package instead of the published CLI, and the check will fail oddly** — something like `could\r\nnot determine executable to run`, or the shell reporting `rendemo` as an unrecognized command — even\r\nthough nothing about the tour or the lockfile is wrong. You cannot know in advance which repo you are\r\nin, so if `npx rendemo check` fails in a way that doesn't look like a real step failure, read the repo\r\nroot's `package.json` `name` field. If it is `rendemo`, either install the CLI as a devDependency and\r\nrun `./node_modules/.bin/rendemo check`, or use whatever script that repo defines for the check (the\r\nRendemo repo itself, whose package is named `rendemo-app` and does *not* collide, offers\r\n`npm run tour:check`). If the name isn't `rendemo`, don't assume this is the cause — report the actual\r\nerror instead.\r\n\r\n`--help` and `--version` both exit 0.\r\n\r\nIf you cannot run it, say so plainly rather than reporting the tour as verified.\r\n\r\nFailures name what to do:\r\n\r\n- `missing-marker` — a lockfile step has no marker in source. The output names the exact attribute to\r\n  write.\r\n- `duplicate-marker` — the marker appears more than once **in source** and the step has no `match`.\r\n  Either de-duplicate, or the step needs `match` — which means going back to `rendemo_add_tour_step`\r\n  and republishing, then regenerating the lockfile. Never edit the lockfile to add it.\r\n  The inverse has no failure to name it: a marker inside a `.map()` is one occurrence in source, so\r\n  check passes and the tour then finds several elements and gives up. Only step 2's first question\r\n  catches that.\r\n- `orphan-marker` — a marker in source that no step references. Delete it or add the step. **If you\r\n  find orphans you did not create, do not just mention them.** Reporting \"there are six orphan markers\r\n  under `sample-waypoint`, pre-existing, not something I touched\" hands someone a problem and no\r\n  handle. Say what they are, then offer the one command that clears them —\r\n  `npx rendemo remove sample-waypoint --dry-run` — and let them decide. It is their repo and their\r\n  call, but the difference between a finding and a fix is one sentence.\r\n- **An unknown-tour warning on a *passing* run** — source has markers for a tour this lockfile does not\r\n  describe, so nothing about that tour is being checked. Regenerate the lockfile, passing the current\r\n  one, unless another team owns those markers. (The CLI still prints this one warning under its\r\n  pre-rename code name and wording. It is the same check, not a different one.)\r\n\r\n**The blind spot this check has, which `rendemo_list_tours` can see and the check cannot:** a tour\r\nthat is *published* but absent from the lockfile is invisible here. The check only verifies what the\r\nlockfile describes, so a published tour whose markers were stripped from source passes silently —\r\ngreen CI, and a live tour anchored to nothing. If §0's listing showed a published tour that the\r\nlockfile does not mention, say so; it is a real broken state and nothing else will report it.\r\n\r\n### Do not leave the repo knowingly red\r\n\r\nIf your change breaks something in this repo — a CI assertion that counts steps or tours, a snapshot,\r\na fixture — **fix it in the same change.** Flagging it twice and fixing it zero times leaves a branch\r\nthat fails its own check, and \"I'll update that line after you publish\" is a promise the user now has\r\nto remember for you.\r\n\r\nWhen a fix genuinely cannot land yet because it depends on an output that does not exist until after\r\npublish (a lockfile, a plan hash), say exactly that, name the file and line, and **come back to it in\r\nthe same session** once the dependency exists. Ending the session with it still red is not an option;\r\nif you must, the final report has to lead with it, not bury it.\r\n\r\nThe check **prints** each tour's `planHash` and cannot verify it — it is offline, so it has no way to\r\nask whether that is still the published plan. Treat the printed hash as something a human can diff.\r\nThere is no staleness detection here; do not tell the user the check proves the tour is current.\r\n\r\n## 9b. Offer the CI step — do not just tell them it exists\r\n\r\nThe check is only a safety net once it runs on every push. You have already written files into this\r\nrepo; wiring up the one line that runs the check is the same kind of act and the same kind of diff.\r\n\r\n**Detect what they use before offering anything.** Look for, in this order:\r\n\r\n| Found | Where the step goes |\r\n| --- | --- |\r\n| `.github/workflows/*.yml` | a `- run: npx rendemo check` step in the existing job, after `checkout` |\r\n| `.gitlab-ci.yml` | a `script:` line in an existing job |\r\n| `.circleci/config.yml` | a `- run: npx rendemo check` step |\r\n| `Jenkinsfile`, `azure-pipelines.yml`, `.drone.yml`, `bitbucket-pipelines.yml` | say you recognised it and offer the equivalent one-liner |\r\n| nothing | offer a minimal GitHub Actions workflow, and say plainly that you are adding CI to a repo that has none |\r\n\r\n**Never overwrite an existing workflow.** Show the exact diff — one added line in almost every case —\r\nand get a yes. If a step running `npx rendemo check` is already there, say so and add nothing.\r\n\r\nThe step itself, for GitHub Actions:\r\n\r\n```yaml\r\n      - run: npx rendemo check\r\n```\r\n\r\nIt needs `actions/checkout` before it and nothing else: no token, no network, no `npm ci`, no Node\r\nversion pin beyond what the job already has. It exits 1 with a `file:line` when a marked element is\r\ndeleted, which is the entire reason the lockfile exists. Put it **early** in the job — it takes about\r\na second, and failing there beats failing after a full build.\r\n\r\nTwo things to say when you offer it, because both change the answer:\r\n\r\n- **`npx` fetches the CLI on each run** unless they install it. If their CI is offline or pins\r\n  dependencies, offer `npm install -D rendemo` and `npx rendemo check` instead, and mention that\r\n  **0.2.0 or newer** is required for the `rendemo.tours.json` name.\r\n- **A repo whose own `package.json` is named `rendemo`** shadows the CLI; there the step must be\r\n  `./node_modules/.bin/rendemo check` with the devDependency installed.\r\n\r\nIf they decline, do not argue. Say what they are choosing: a deleted element silently breaks the tour\r\nfor every user, and nothing will tell them.\r\n\r\n## Retiring a tour\r\n\r\nTaking a tour offline is only half of retiring it. `rendemo_take_demo_offline` is the **kill switch** —\r\ninstant, ungated, \"stop serving this now\", touches no files, and is the right tool when a live tour is\r\npointing at UI that just moved. **Say its one limit as you use it:** it stops the tour being *served*,\r\nbounded at 30 seconds by the payload's cache — it does **not** reach into a tab where the tour is\r\nalready running, and that visitor finishes it. Someone reaching for a kill switch is reaching for it\r\nunder pressure and needs to know what it does not cover. But its markers stay in source, so\r\n`rendemo check` then fails the build\r\nwith an `orphan-marker` for every one of them: the safety feature turning into a nuisance exactly when\r\nsomeone is backing out.\r\n\r\n**The user can do this without you, and they should be told so once.** `npx rendemo remove <tour-slug>`\r\nstrips every marker for that tour from their source and takes its entry out of `rendemo.tours.json`,\r\noffline, with no token and no MCP — so removal keeps working in a checkout with no Rendemo sign-in at\r\nall. `--dry-run` first, `--all` for every tour. It prints the embed element rather than deleting it\r\n(shared layout, their call), leaves test files alone and names them, and exits 1 rather than guessing\r\nat a `demo()` call it cannot remove whole. It does not take the tour offline server-side — that needs\r\nauth — but once the markers and the element are gone nothing is asking for the payload. Requires\r\n**rendemo 0.5.0 or newer**.\r\n\r\nUse it when you are removing a tour from a repo you are already working in: it does the tedious half\r\n(finding every marker) in one pass and produces the same reviewable diff you would have written.\r\n\r\n`rendemo_remove_tour({ projectId, lockfile })` is the retirement done through the MCP, and is what to\r\nuse when the tour must also stop being **served**. It does all three halves in one reviewable change:\r\n\r\n1. takes the tour offline (it stops being served within 30 seconds),\r\n2. returns **every** `data-rendemo` marker to strip from source,\r\n3. returns `rendemo.tours.json` with this tour's entry removed and **every other tour preserved** —\r\n   pass the current file's contents as `lockfile`, or it returns only the markers and you edit the\r\n   file yourself. It refuses an unparseable lockfile rather than replacing it.\r\n\r\nThen remove the `<rendemo-demo … mode=\"tour\">` element if it was placed for this tour alone, and run\r\n`npx rendemo check`: with the markers gone and the entry gone it passes, and with either half missing\r\nit does not. That asymmetry is the point of doing both in one commit.\r\n\r\n**Nothing is deleted.** The plan and the published plan are kept, so publishing again later serves the\r\nidentical tour. Say that — \"retire\" sounds permanent and it is not.\r\n\r\n## 10. Install the element\r\n\r\nA published, verified tour still shows nobody anything until the element is on the page. If you placed\r\nit in a dev build for the preview in step 6, this is the point at which it is safe to ship it — the\r\ntour is published now, so a real visitor gets the tour rather than the \"could not be loaded\" note.\r\n\r\nHand the user the two lines and say where they go — the layout or route that the tour's **entry route**\r\nbelongs to, so the element is present when the tour starts and stays mounted across the pages the steps\r\nspan:\r\n\r\n```html\r\n<script src=\"https://www.rendemo.com/embed.js\"\r\n        data-demo=\"<workspace>/<tour-slug>\" data-mode=\"tour\" async></script>\r\n```\r\n\r\n`mode=\"tour\"` renders no box and reserves no space — the card is drawn in its own fixed layer. The\r\nelement fetches a second script (`/embed-tour.js`) and the tour's payload from\r\n`/site/<workspace>/<tour-slug>/tour`, both public and cacheable. If the tour cannot be resolved the\r\nelement says so, in the page and in the console — it never silently renders nothing.\r\n\r\n`mode=\"guide\"` is still accepted as a silent alias of `mode=\"tour\"`, because a customer's HTML can be\r\nserved from a CDN cache long after `embed.js` updates. Never write it into new code.\r\n\r\nThe optional attributes, all three of them:\r\n\r\n```html\r\n<script src=\"https://www.rendemo.com/embed.js\"\r\n        data-demo=\"<workspace>/<tour-slug>\" data-mode=\"tour\"\r\n        data-user=\"u_123\"                    <!-- opt-in identity; any opaque string you choose -->\r\n        data-routes=\"/app,/app/projects\"     <!-- pages this may run on at all -->\r\n        data-when=\"always\"                   <!-- replay a finished tour; default is once -->\r\n        async></script>\r\n```\r\n\r\nWriting the element yourself stays correct where the script tag cannot sit at the right place — a\r\nReact layout, a template slot. Every `data-*` above is the same attribute without the prefix, and\r\n`src` aliases `demo`: `<rendemo-demo src=\"<workspace>/<tour-slug>\" mode=\"tour\">`.\r\n\r\nFacts a host integrating this will hit immediately, so say them:\r\n\r\n- **Mounting is starting.** There is no `open` attribute and no start button; the tour begins in\r\n  `connectedCallback` and tears down on unmount. A host that gates the tour behind a button expresses\r\n  that by rendering the element or not — and that conditional render **is** the targeting primitive.\r\n- **A finished or dismissed tour renders nothing, forever — unless you say `when=\"always\"`.** The\r\n  default is right for onboarding and wrong for a \"Take the tour\" button; `when=\"always\"` is that\r\n  button, and it restarts a finished or dismissed tour from the top while still resuming a run that is\r\n  genuinely in progress. Reaching into `localStorage` to delete the progress key before mount still\r\n  works and is what `components/StudioTour.tsx` predates this attribute by doing, but it is no longer\r\n  necessary. (That key keeps its pre-rename spelling on purpose — renaming it would restart every tour\r\n  already in progress in every visitor's browser.)\r\n- **`routes` is evaluated per navigation, `user` and `when` at mount.** A `user` swap remounts the\r\n  tour (somebody signing in), so it reads that person's progress rather than the last one's.\r\n- **`user` does not make progress follow anyone.** It keys and tags progress, but the storage is still\r\n  `localStorage` — so it is per person *within a browser*, not a server-side profile. A second device,\r\n  a private window or cleared storage restarts the tour from step one. Say this whenever you hand over\r\n  the `user` attribute; someone reading `user=\"u_123\"` will otherwise assume it syncs, and it does not.\r\n- **There is no rule engine behind any of this.** No \"new users only\", no per-plan, per-role or\r\n  feature-flag condition, no percentage rollout. Who sees the tour is the conditional render above;\r\n  `routes` filters pages, not people. Never describe it as segmentation.\r\n\r\nDo not add it for them without asking: it is a change to a shared layout that affects every visitor of\r\nthose routes, and `routes` narrows *where* it runs, not *who* it runs for.\r\n\r\n**Say how to undo all of this, in one line, here.** You have just written attributes into their source\r\nand a lockfile into their repo, and \"how do we get this out again?\" is a fair question to answer\r\nbefore it is asked rather than after:\r\n\r\n```bash\r\nnpx rendemo remove <tour-slug> --dry-run\r\n```\r\n\r\nIt strips every marker for the tour and its lockfile entry, offline, with no token — so it keeps\r\nworking whether or not Rendemo is still in the picture. One line. Do not expand it into a paragraph\r\nabout kill switches; that belongs in \"Retiring a tour\", where someone who actually wants it will be.\r\n\r\n## Report honestly at the end\r\n\r\nState: the tour slug, the steps and their markers, that the user saw the preview and approved it, that\r\nit is published, that the lockfile is committed, whether the check actually ran and its exit code, and\r\nwhether the element is installed or still needs to be.\r\n\r\nThree more, each of which is a thing the user would otherwise discover later:\r\n\r\n- **Anything still red or still owed.** A CI assertion you had to defer, a step you could not resolve,\r\n  a decision you defaulted because it came back unanswered. Lead with it. A report that reads as\r\n  finished while the branch fails its own check is the one failure mode that costs trust rather than\r\n  time.\r\n- **The gate, if the tour is audience-scoped.** Name the component and the condition, and say once\r\n  that it is host code no Rendemo surface checks — not to hedge, but because it is the line in the\r\n  change that needs a human reviewer.\r\n- **Which steps the reviewer had to be signed in to see**, if the probe found any. It explains the\r\n  preview they just walked and stops the same question next time.\r\n\r\n**Do not replay all five limits here.** Each was said at the moment it mattered, and a closing recital\r\nof things already disclosed is the same wall of text moved to the end — it reads as hedging a tour you\r\njust shipped. Name only the two that govern what they do *next*, in one line: **who sees it is your own\r\nconditional render** (there is no rule engine, and `routes` filters pages, not people), and **progress\r\ndoes not follow anyone to a second device**.\r\n\r\nThe end of this procedure is a live, decay-proof, measured tour for everyone who loads those routes and\r\nis not filtered out by your own conditional render — not a segmented onboarding programme.\r\n"
}

SHA-256: 60838c48c460ab14104ce504a1e9a051201b9d61ed5716df5516e7c5ac573e25