Skip to content
Resource
DocsTourSlicesBest PracticeAgentsInstall

Command Palette

Search for a command to run...

New
  • Changelog

Changelog

Release notes

Changelog

What changed in each rr release — page 11 of 25. Full prose history in CHANGELOG.md.

JUN282026
Fixslices@async-error-huntJun 28, 2026

Async error handling — 6 unhandled rejections / silent failures + blob leaks fixed (image-editor, notion-shell)

Swept slice components for unhandled errors on user-triggered async paths + floating promises, each adversarially verified (0 rejected). 6 real bugs fixed, all P3 but reachable: image-editor menu-bar + top-bar openImage/openFile awaited loadImage() with no try/catch — an undecodable picked image (corrupt, truncated, HEIC) rejected with an unhandled promise, no layer added, no feedback, AND the createObjectURL blob leaked; now wrapped with a catch that revokes the blob. image-editor removeBg() (both bars) called removeImageBackground() — which dynamically downloads an ONNX model + WASM on first run — with try/finally but NO catch, so an offline/unsupported first use escaped as an unhandled rejection (busy reset, but the error was lost with zero feedback); added the catch. notion-shell CodeBlock copy + NotionBlock copy-link awaited navigator.clipboard.writeText with no guard — rejects in an insecure context / when the document isn't focused / on denied permission, leaving an unhandled rejection and (for CodeBlock) the copied state never set; both guarded. These slices have no error-surface channel, so the fixes prevent the unhandled rejection / stuck state / blob leak (gate-permitted swallow). slices:check 70 ok + tsc green.

Fixes

  • image-editor — open-image (loadImage) and remove-background now catch rejections + revoke leaked blob URLs
  • notion-shell — code-block copy + block copy-link clipboard writes guarded against unhandled rejection
JUN282026
Fixcli@1.14.1 · mcp@1.2.4Jun 28, 2026

CLI + MCP defect hunt — reflected XSS in the MCP OAuth consent page closed; CLI boolean-flag parsing fixed

First defect sweep of the published packages (packages/cli + packages/mcp — bugs here ship to every npx rr / MCP user). 10 module groups audited, every finding adversarially verified; 8 candidates surfaced, 6 correctly rejected as unreachable/dev-only (dead removeSkill, conflict-gated compose accepted-set, curated-archive dna, quote-free sync-skills regex, gen-manifest write-ordering, self-inflicted init path). 2 real bugs fixed: (1) SECURITY — the MCP OAuth 2.1 consent page rendered the attacker-influenced redirect_uri inside an onclick JS-string sink (window.location='…'); its esc() HTML-encodes the apostrophe to &#39;, which the HTML parser decodes back to a literal ' before the JS runs, so a redirect_uri like https://localhost/'-alert(document.domain)-' (valid https URL, passes the protocol check) breaks out and executes arbitrary JS on the MCP origin when the victim clicks Deny — reflected XSS. The Deny button is now an <a href> (navigation context where HTML-escaping is correct), styled to match. (2) CLI — parseFlags consumed the next token as a value for EVERY --flag (no boolean notion), so `rr add my-slice --force ./apps/web` swallowed ./apps/web as the value of --force and silently installed into the cwd instead. Replaced with a value-flag allowlist (target/template/category/at/skills/features for cli.js; from/to/repo-root for the duplicate parser in migrate-load.mjs) so boolean flags before a positional no longer eat it. Verified: XSS render check + parseFlags behavioral check + CLI vitest 57 pass. CLI 1.14.0→1.14.1, MCP 1.2.3→1.2.4 (publish pending OTP).

Fixes

  • MCP — reflected XSS in the OAuth consent page Deny control closed (onclick JS-string sink → <a href>)
  • CLI — boolean flag before a positional no longer swallows it (value-flag allowlist in both parseFlags copies)
JUN282026
Fixslices@a11y-depth-2Jun 28, 2026

a11y depth pass 2 — 22 fixes: form controls labelled + click-only tabs/rows made keyboard-accessible

Second deep a11y sweep (form controls without labels + click handlers on non-interactive elements), each finding adversarially verified, 0 rejected. FORM LABELS: 15 inputs/textareas that exposed only a placeholder (not an accessible name) got aria-labels — landing-sections newsletter, vector-search, data-table filter, comments reply, assistant composer + the agent/skill/automation Field forms, browser omnibar, ai-chat + ai-router chat fabs, user-management members search, reel-editor + image-editor AI panels, image-editor properties panel (11 transform/stroke/brush fields whose visible <Label> had no htmlFor), app-store create form, file-explorer rename. KEYBOARD ACCESS: controls that were click-only (no role/tabIndex/onKeyDown) and thus unusable by keyboard or screen-reader users got proper key handling — code-editor + browser tab strips (role=tab + Enter/Space → select), media-studio + image-editor layer rows (role=button + guarded Enter/Space → select), and file-explorer's list-view row was converted from a bare <div> to a <Button> mirroring its own grid branch (native keyboard access, layout unchanged). One control skipped: skill-form's "Allowed tools" wraps a custom ToolPicker (not a labelable input). slices:check 70 ok + tsc green.

Keyboard access (was mouse-only)

  • code-editor + browser — file/browser tab switching now keyboard-operable (role=tab, Enter/Space)
  • file-explorer — list-view row converted to a Button (keyboard select/open) + rename field labelled
  • media-studio + image-editor — layer rows selectable via keyboard (guarded so nested inputs still work)

Form labels (placeholder-only → aria-label)

  • landing-sections, vector-search, data-table, comments, browser, ai-chat, ai-router, user-management, reel-editor, app-store — input/textarea accessible names added
  • assistant — agent/skill/automation Field forms + image-editor properties panel (11 fields) labelled
JUN282026
Fixslices@defect-hunt-r3Jun 28, 2026

Call-graph + a11y depth — aiChat dead nonexistent-module ref removed, 6 more icon-button/dialog-title a11y misses

Two more verified passes. CALL-GRAPH (convex/** excluded from root tsc, so gate-invisible): swept every ctx.run*/scheduler call against its target validator. ai-chat chat action called api.aiConfig.get — a module that exists NOWHERE in the tree (a leftover from the source project's agencyAiConfig singleton, referenced only in that one file); on the ANTHROPIC_API_KEY-set path (the real use case, outside the try) it threw 'Could not find public function'. Removed the dead runQuery + now-unused api import, inlined the existing default system prompt. (Noted but deliberately NOT changed: seo slice's generate action references api.slices.auth.me / api.slices.seo.* — the whole action targets the consumer-side `slices.*` codegen namespace, not rr's `features.*`, so it's a consumer-composition contract, not an rr-repo bug.) A11Y depth (round 1 only scratched this, capped at 10): 5 more icon-only controls with no accessible name got aria-labels — code-editor tab close (dynamic label w/ unsaved state), assistant glyph picker (matching its sibling ColorPick), appshell window-overview reveal + dock app links, notion editor toggle block; and icon-picker's dialog-fallback DialogContent had no DialogTitle (Radix a11y violation + runtime warning) — added an sr-only title. slices:check 70 ok + tsc green.

Runtime correctness

  • ai-chat — chat action no longer calls a nonexistent api.aiConfig.get module (threw on every keyed request); default prompt inlined

Accessibility depth

  • code-editor — tab close button labelled (with unsaved-changes state)
  • assistant — glyph picker buttons labelled
  • appshell — window-overview reveal + dock app links labelled
  • notion — editor toggle-block collapse/expand labelled
  • icon-picker — dialog-fallback gets a required (sr-only) DialogTitle
JUN282026
Fixslices@defect-hunt-r2Jun 28, 2026

Convex security sweep R2 — 3 runtime null-throws (wrong internal API path), tenant priv-esc + owner-write IDOR, private-row leak

Per-feature-dir security sweep across all 20 convex/features handlers, each finding adversarially verified. CORRECTNESS (these crashed at runtime and shipped broken to every consumer — convex/** is excluded from root tsc so neither typecheck nor the import-gate caught them): ai callModel, bookings calWebhook, and the whole newsletter broadcast fanout referenced internal.features.<x>.MUTATIONS/QUERIES.fn (plural) but every module file is mutation.ts / query.ts (singular), so the generated API node was undefined and each ctx.runMutation/runQuery threw 'Cannot read properties of undefined' — for ai, AFTER the paid generateText() spend; for bookings, on every Cal.com webhook; for newsletter, before a single email sent. Fixed plural→singular (7 refs) + the same typo in a testimonials operator doc-comment. SECURITY: user-management hierarchy linkTenant gated members.manage only on the parent tenant while childTenantId was an arbitrary string — a tenant admin could attach ANY victim tenant as a child and then read its members (getAccessMatrix) or inject a privileged invite (sendHierarchyInvite); now requires members.manage on BOTH ends. payment recordDokuPending patched an existing order's amount + let the caller hijack its checkoutUrl with no owner check; now blocks cross-owner patch (guest + owner re-creates still pass). activity get was a public query returning private rows by id, bypassing the slice's own public-only contract (listAll is internal, listPublic filters) — now returns null for non-public rows. Deliberately SKIPPED: pages-cms listAll draft-visibility (non-tenant/actor data on a documented copy-source public read where the consumer owns the auth model — same call left as-is for the library twin).

Runtime null-throws (shipped broken)

  • ai-router — callModel logged usage via a plural internal API path → threw after paid spend; fixed to singular
  • cal-com-booking — calWebhook crashed on every delivery (plural mutations path); fixed to singular
  • resend-newsletter — broadcast fanout threw before sending (4 plural query/mutation paths); fixed to singular

Security (copy-source)

  • user-management — linkTenant now requires members.manage on the child tenant too (closes cross-tenant disclosure + invite priv-esc)
  • doku-payment — recordDokuPending blocks cross-owner amount patch + checkout-URL hijack
  • activity — public get no longer returns private rows by id
JUN282026
Fixslices@defect-hunt-r1Jun 28, 2026

Defect sweep R1 — 4 cross-tenant IDOR holes closed in copy-source mutations, comment-resolve authz, 8 a11y/leak fixes

Multi-agent defect hunt (a11y / resource-leak / effect-correctness / null-safety / convex-correctness), each finding adversarially verified before fix. SECURITY (copy-source convex, inherited by every consumer via npx rr add): user-management mutations cancelInvite / resendInvite / removeTeam / addTeamMember / removeTeamMember all gated requirePermission on args.tenantId but then operated on a raw inviteId/teamId with no ownership match — a tenant-A admin could delete/rotate/mutate tenant-B invites, teams, and team memberships (cross-tenant IDOR). Each now loads the doc and verifies doc.tenantId === args.tenantId before touching it. comments.resolve only checked authentication, not ownership (unlike update/remove) — any signed-in user could toggle resolvedAt on any thread; now mirrors the c.actorId guard. A11Y: icon-only buttons with no accessible name got aria-labels — browser history close, assistant step reorder/delete, image-editor bold/italic + AI send, notion-database calendar prev/next-month + property rename/delete, media-viewer toolbar (IconBtn now forwards its tooltip label to aria-label). LEAK: image-picker UploadTab created a blob URL per pick and never revoked it — added a cleanup effect keyed on the preview URL. effect-correctness + null-safety dimensions came back clean. slices:check 70 ok + tsc green.

Security (copy-source)

  • user-management — 4 cross-tenant IDOR holes closed: cancel/resend invite + removeTeam + add/removeTeamMember now verify doc.tenantId === args.tenantId
  • comments — resolve now checks thread ownership (c.actorId), matching update/remove

Accessibility + leak

  • browser — history close button labelled
  • assistant — step up/down/delete buttons labelled
  • image-editor — bold/italic (+aria-pressed) + AI send button labelled
  • notion-database — calendar month nav + property rename/delete labelled
  • media-viewer — IconBtn forwards tooltip label to aria-label (whole toolbar)
  • image-picker — UploadTab revokes blob URLs on re-pick/unmount (no leak)
JUN192026
Featuretour@finisherJun 19, 2026

Grand Tour ships — rr is now a slice picker + ONE /tour showcase; the OS-template catalog is retired

The Grand Tour (P0–P7) lands and replaces the old multi-taxonomy catalog. rr is now a pure slice picker ("printilan") plus ONE curated showcase: /tour walks every slice in context across six Acts — I Marketing, II OS & App Shell, III Media, IV AI, V Content, VI Platform/Auth & Commerce — driven by lib/content/tour.ts, with a per-Act rail on the index and a deep-linkable page at /tour/<id>. The content slice (Act V) gains pages-cms, the generalized CMS extracted from the retired OS-template admin surfaces. The /layouts + /templates catalog is decommissioned: lib/content/layouts.ts data is emptied, /layouts + /templates (plus the old /preview/<os-template> full-app and per-section demos) 308-redirect to /tour, and ~871 demo files under app/preview/** + components/templates/** were deleted. The 8 OS demos stay live EXTERNALLY at demo-*.rahmanef.com, served from their own Vercel dev-lab repos via the untouched proxy.ts host rewriter. Finisher pass adds a /tour smoke test to the Playwright e2e suite, rewrites the prose docs that still described /layouts + the T1–T5 OS templates as live (README, STRUCTURE, PROGRESS, eject-spec, subdomain-routing), and deletes the per-template playbook set under docs/templates/ in favour of one docs/tour.md pointer. validate:all stays green.

Highlights

  • /tour — 6 Acts: Marketing, OS & App Shell, Media, AI, Content, Platform/Auth & Commerce (each Act deep-linkable at /tour/<id>)
  • pages-cms — generalized CMS slice (Act V), extracted from the retired OS-template admin surfaces
  • /layouts + /templates decommissioned: layouts.ts emptied, routes 308→/tour, ~871 demo files deleted
  • 8 OS demos stay live externally at demo-*.rahmanef.com (proxy.ts untouched)
  • docs reflow: stale /layouts + T1–T5 prose rewritten; docs/templates/ playbooks deleted → docs/tour.md; /tour e2e smoke test added
JUN112026
Fixslices@copy-source-hardeningJun 11, 2026

Track A — copy-source security: callModel key-guard, payment status proxy closed, listAll internal; catalog versions synced

Final track of the 2026-06-11 audit. These functions are copy-source (never on rr's own backend — deploy allowlist keeps them off), but every consumer inherits them via npx rr add. ai-router 0.5.0: callModel was the one paid-AI action with a non-null-asserted OPENROUTER_API_KEY and no guard — now key-guarded like its sibling aiChat ({ ok:false, notice } when unset; success is { ok:true, text }), with documented rate-limit wiring (ai: prefix consume) left to composition to avoid a hard peer coupling. doku-payment 0.4.0: getPaymentStatus proxied DOKU's status API for ANY orderId — cross-tenant order probing plus upstream creds burn. It now requires the order to exist locally first (owned orders → caller must be the owner; guest orders → the unguessable orderId stays the capability, same rule as getOrderByOrderId); unknown/unauthorized/unconfigured all return null. activity 0.3.0: listAll returned private-visibility rows as a public query — now an internalQuery the consumer wraps in their own auth-gated query, the same pattern as the slice's mutations. notion-shell: KaTeX parse errors echo the user's TeX source — the error span is now HTML-escaped before dangerouslySetInnerHTML (self-XSS hygiene). Plus the durable catalog fix: all 50 drifted lib/content/slices.ts versions synced to slice.json (report-slices-drift now reads zero), with gen-manifest already sourcing versions from slice.json directly.

JUN112026
Improvementsite@builder-uxJun 11, 2026

Bundle Builder — compat notes get a path forward; project form validates inline

Track B of the 2026-06-11 audit. Compatibility notes in the command panel now tell the user what to DO: every warning carries an action line (incompatible → drop the slice or re-pick the template; warn → wire manually after scaffolding) plus a View-slice link to the docs page. An incompatible pair escalates the whole box to blocker-red — it was rendering as a soft amber note while the emitted command was broken. Project form: the emitted command always uses a sanitized app name, so typing 'My App!' silently scaffolded 'my-app-' — the form now shows the actual folder name when it differs, and flags malformed owner emails (aria-invalid + inline error) before they land in a scaffold. Pre-push vitest gate also retries once now: a known environmental flake exits 1 with all 468 tests green (~1/4 of hook runs); a real failure stays red on both runs.

JUN112026
Improvementmcp@1.2.3Jun 11, 2026

CLI network errors + MCP structured errors, stale-loader fix, manifest version SSOT

Track C continued (docs/audit-2026-06-11.md). CLI: every tiged clone now goes through cloneWithRetry — one silent retry on transient network errors, then an actionable message (ENOTFOUND → check connectivity; 404/ENOENT → verify slug + repo is public) instead of a raw git stack trace. MCP 1.2.3: rr_get / rr_get_slice return structured not_found payloads ({ error, message, didYouMean, try }) so a calling LLM can branch and recover instead of echoing a string; the whole tool dispatcher is try/catch-wrapped (a throwing handler — e.g. bad workflow kind — becomes a tool error, not a dead stdio server); tool descriptions rewritten with explicit use-when guidance disambiguating the three composers (init vs add vs compose_app) and rr_get vs rr_get_slice; stray kitab terminology dropped. Two real bugs found while smoking it: the data-loader preferred an installed rahman-resources over the sibling monorepo CLI — a stale 0.9.2 snapshot (8 slices) silently shadowed the 68-slice source of truth in local dev (now sibling-first); and gen-manifest took versions from the hand-curated catalog, shipping stale versions in the distributed manifest — it now reads each slice.json (the version SSOT) directly. report-slices-drift pairing fixed (entry-window matching): true catalog drift is 50/68, previously misattributed.

NewerPage 11 / 25Older