Skip to content

feat(billing-3): billing, validation, shell infra, and SDK sync - #1078

Merged
Rod-Christensen merged 13 commits into
developfrom
feat/billing-3
Jun 4, 2026
Merged

Rod-Christensen merged 13 commits into
developfrom
feat/billing-3

Conversation

@Rod-Christensen

@Rod-Christensen Rod-Christensen commented Jun 3, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Billing & checkout: shared PlanPicker component with interval toggle, action plans (Free/Enterprise CTAs), plan ordering by metadata, simplified CheckoutModal
  • Subscription gate: ShellLayout blocks app loading for unsubscribed orgs
  • Canvas validation overhaul: replaced cfworker validator with AJV — cfworker silently crashed on engine's non-standard "object" schema key, so nodes with missing API keys never showed red gear. Added stripEmptyStrings so required catches empty fields. Added useEffect([servicesJson]) re-validation for async catalog load
  • Canvas QA: forward drag events through CreateNodePanel backdrop (PR fix(canvas): forward drag events through CreateNodePanel backdrop #1076)
  • Auth consolidation: ConnectionManager accepts authProvider, CloudAuthProvider uses location.replace() to fix browser back button landing on expired Zitadel page
  • Shell infra: MF app path /shell/apps/ → /apps/, builder auto-discovery of *-ui modules, ui:register command, MF remote logging, post-auth re-registration fix
  • Model server: SentencePiece slow tokenizer fallback
  • SDK sync: Python billing types updated to match TypeScript (PlanAction, expanded StripePlan, CreditBalance labels)
  • Documents: skip writing isNew (untitled) docs to disk
  • CSP: added 'unsafe-eval' for AJV in VSCode webview

Test plan

  • Open checkout modal for a 2-plan app (Brandy) — should show 2 cards, no toggle
  • Open checkout modal for PipeBuilder — should show Free + paid tiers + Enterprise with Monthly/Annual toggle
  • Click Free → opens GitHub; click Enterprise → opens mailto
  • Select paid plan → Continue → Stripe payment form works
  • Open a pipeline with unconfigured nodes → red gear icons appear, snackbar shows count
  • Configure a node (fill API key) → red gear clears
  • Browser back button after auth → does NOT land on expired Zitadel page
  • Drag node from CreateNodePanel when backdrop is visible → drops correctly on canvas
  • Unsubscribed org trying to open a gated app → blocked by subscription gate
  • ./builder client-typescript:build — no TS errors
  • Python SDK imports: from rocketride.types import PlanAction, StripePlan — no errors

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added subscription gating for apps with "Subscription required" messaging
    • Introduced announcement system with time-based filtering and Markdown support
    • New plan picker component for improved checkout experience with interval toggle support
    • Added plan action support for alternative click behaviors (links/mailto)
  • Bug Fixes

    • Improved OAuth redirect handling to prevent back-button issues
    • Enhanced tokenizer loading with fallback mechanisms
    • Fixed document save logic for untitled documents
  • Documentation

    • Updated API endpoint references from cloud.rocketride.ai to api.rocketride.ai across all client libraries and guides
  • Chores

    • Enhanced Content Security Policy directives
    • Improved app registration and module discovery flow

@github-actions github-actions Bot added docs Documentation builder Node builder tooling and ./builder workflows labels Jun 3, 2026
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ad39f236-c782-4f4d-b69d-1db783171e8f

📥 Commits

Reviewing files that changed from the base of the PR and between 1a00242 and 6da6cfd.

📒 Files selected for processing (45)
  • apps/chat-ui/scripts/tasks.js
  • apps/dropper-ui/scripts/tasks.js
  • apps/shell-ui/src/auth/CloudAuthProvider.ts
  • apps/shell-ui/src/components/layout/Shell.tsx
  • apps/shell-ui/src/components/layout/ShellLayout.tsx
  • apps/shell-ui/src/components/layout/Sidebar.tsx
  • apps/shell-ui/src/connection/connection.ts
  • apps/shell-ui/src/lib/Documents.tsx
  • apps/shell-ui/src/lib/appLoader.ts
  • apps/vscode/rsbuild.config.mjs
  • apps/vscode/src/config.ts
  • apps/vscode/src/providers/ProjectProvider.ts
  • apps/vscode/src/providers/template.html
  • docs/README-python-client.md
  • docs/README-typescript-client.md
  • docs/agents/ROCKETRIDE_PIPELINE_RULES.md
  • docs/agents/ROCKETRIDE_QUICKSTART.md
  • docs/agents/ROCKETRIDE_python_API.md
  • docs/agents/ROCKETRIDE_typescript_API.md
  • packages/ai/src/ai/common/models/transformers/transformers.py
  • packages/ai/src/ai/constants.py
  • packages/ai/src/ai/modules/shell/shell.py
  • packages/client-python/src/rocketride/core/constants.py
  • packages/client-python/src/rocketride/types/__init__.py
  • packages/client-python/src/rocketride/types/billing.py
  • packages/client-python/tests/RocketRideClient_test.py
  • packages/client-typescript/src/cli/rocketride.ts
  • packages/client-typescript/src/client/client.ts
  • packages/client-typescript/src/client/constants.ts
  • packages/client-typescript/src/client/types/billing.ts
  • packages/client-typescript/tests/RocketRideClient.test.ts
  • packages/shared-ui/src/components/canvas/components/FlowCanvas.tsx
  • packages/shared-ui/src/components/canvas/components/panels/node-config/NodeConfigPanel.tsx
  • packages/shared-ui/src/components/canvas/context/FlowGraphContext.tsx
  • packages/shared-ui/src/components/canvas/util/csp-safe-validator.ts
  • packages/shared-ui/src/components/canvas/util/rjsf.ts
  • packages/shared-ui/src/components/sidebar-footer/SidebarFooter.tsx
  • packages/shared-ui/src/hooks/useAnnouncements.ts
  • packages/shared-ui/src/index.ts
  • packages/shared-ui/src/modules/checkout/CheckoutModal.tsx
  • packages/shared-ui/src/modules/checkout/PlanPicker.tsx
  • packages/shared-ui/src/modules/checkout/index.ts
  • packages/shared-ui/src/modules/checkout/types.ts
  • scripts/lib/registerApp.js
  • scripts/tasks.js

📝 Walkthrough

Walkthrough

This PR introduces a large-scale platform update spanning shell authentication, task orchestration, billing contracts, checkout UI refactoring, shared announcement system, form validation migration, cloud endpoint migration, and AI runtime improvements. Key changes include a two-step checkout flow with new plan picker component, subscription gating in shell, OAuth provider abstraction, cloud endpoint consolidation to api.rocketride.ai, dynamic UI task discovery, and migration from CSP-safe to AJV8 form validation with snackbar feedback.

Changes

Platform-wide feature and infrastructure updates

Layer / File(s) Summary
Static asset routing, app manifest layout, and task orchestration
packages/ai/src/ai/modules/shell/shell.py, scripts/tasks.js, scripts/lib/registerApp.js, apps/chat-ui/scripts/tasks.js, apps/dropper-ui/scripts/tasks.js
Shell/app static serving is split by route with separate handlers; app registration and scripts remove hardcoded UI module lists in favor of dynamic discovery; per-app register tasks are added for build-time app metadata seeding.
Cloud endpoint migration and supporting docs/tests
apps/vscode/rsbuild.config.mjs, apps/vscode/src/config.ts, packages/client-python/src/rocketride/core/constants.py, packages/client-typescript/src/client/constants.ts, docs/README-*.md, docs/agents/*.md, packages/client-python/tests/, packages/client-typescript/tests/
Default cloud URIs move from cloud.rocketride.ai to api.rocketride.ai across VSCode/Python/TypeScript client constants, SDK documentation, agent quickstart docs, and URI normalization tests.
VSCode webview CSP and security policy
apps/vscode/src/providers/template.html, apps/vscode/src/providers/ProjectProvider.ts
Webview template CSP metadata is added with directives for script, style, font, connection, and image resources; project webview script policy is updated to include unsafe-eval.
Shell OAuth provider injection and bootstrap wiring
apps/shell-ui/src/auth/CloudAuthProvider.ts, apps/shell-ui/src/connection/connection.ts, apps/shell-ui/src/components/layout/Shell.tsx
CloudAuthProvider performs OAuth redirects using window.location.replace; connection manager accepts and delegates to authProvider; bootstrap wiring supplies provider during shell init; OAuth PKCE flow becomes legacy fallback with dynamic imports; bootstrap logging is enhanced throughout OAuth, token, and finishConnect paths.
Shell runtime subscription gating, document safety, and UI polish
apps/shell-ui/src/components/layout/ShellLayout.tsx, apps/shell-ui/src/lib/Documents.tsx, apps/shell-ui/src/lib/appLoader.ts, apps/shell-ui/src/components/layout/Sidebar.tsx
ShellLayout adds subscription gate that emits shell:subscribe when active app is unsubscribed; saveDocument skips new documents; appLoader adds registration logging; Sidebar icon resolves active app branding.
Billing type contracts across Python, TypeScript, and shared UI
packages/client-python/src/rocketride/types/billing.py, packages/client-typescript/src/client/types/billing.ts, packages/shared-ui/src/modules/checkout/types.ts
PlanAction type and expanded plan/billing fields (credits, labels, order, description) are introduced across Python/TypeScript SDKs and shared-ui billing types, with matching re-exports in type aggregators.
PlanPicker component and CheckoutModal two-step refactor
packages/shared-ui/src/modules/checkout/PlanPicker.tsx, packages/shared-ui/src/modules/checkout/CheckoutModal.tsx, packages/shared-ui/src/modules/checkout/index.ts
New PlanPicker component handles billing period toggle, card selection, and action-plan navigation; CheckoutModal delegates step 1 to PlanPicker, restructures single-column layout with header/body, moves recap to step 2, and pre-selects first checkout-able plan; exports are updated to include PlanPicker and PlanAction.
Remote announcements hook and sidebar rendering with markdown
packages/shared-ui/src/hooks/useAnnouncements.ts, packages/shared-ui/src/components/sidebar-footer/SidebarFooter.tsx, packages/shared-ui/src/index.ts
New useAnnouncements hook fetches and caches announcements from GitHub with 1-hour TTL, filters by valid_from/valid_until time window, and resolves relative image URLs; SidebarFooter replaces hardcoded announcements with hook-based data and renders markdown with remark-gfm/rehype-sanitize in both flat and popup ticker modes; announcements exports are added to index.
Canvas form validation switch and snackbar state management
packages/shared-ui/src/components/canvas/util/rjsf.ts, packages/shared-ui/src/components/canvas/context/FlowGraphContext.tsx, packages/shared-ui/src/components/canvas/components/FlowCanvas.tsx, packages/shared-ui/src/components/canvas/components/panels/node-config/NodeConfigPanel.tsx
RJSF validator switches from CSP-safe cfworker implementation to @rjsf/validator-ajv8 package; empty-string stripping is added to treat blank fields as missing for required validation; FlowGraphContext loadData returns unconfigured node count and manages configSnackbar state with 6-second auto-hide; FlowCanvas and NodeConfigPanel are rewired to use context-provided snackbar and validator.
AI tokenizer fallback chain and scaling constant rename
packages/ai/src/ai/common/models/transformers/transformers.py, packages/ai/src/ai/constants.py
Transformers tokenizer loading adds fallback chain: slow tokenizer (use_fast=False), then AutoProcessor, with debug logging at each step; model scaling constants are renamed from queue-based thresholds to drain-time-based timing with consistent _S suffix.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

  • rocketride-org/rocketride-server#973: Both PRs update the sidebar announcements ticker in packages/shared-ui/src/components/sidebar-footer/SidebarFooter.tsx, changing how announcement content and links are rendered in the ticker display.
  • rocketride-org/rocketride-server#767: Both PRs modify the shared-ui RJSF validation stack in packages/shared-ui/src/components/canvas/—switching validator implementations between CSP-safe and AJV8-based approaches.

Suggested labels

module:client-python, module:client-typescript, module:vscode, module:ai, module:ui

Suggested reviewers

  • jmaionchi
  • stepmikhaylov
  • dsapandora

Poem

🐰 A rabbit's ode to this grand PR:

Tasks orchestrated, endpoints moved to the light,
Plans picker dances in checkout's sight,
Announcements whisper from GitHub's fold,
Forms validate faster, subscription gates hold.
The shell boots with auth, the AI learns to try—
A grand refactor reaching toward the sky! 🚀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title comprehensively covers the PR's main objectives: billing (PlanPicker, CheckoutModal, billing types), validation (cfworker→AJV, stripEmptyStrings, FlowGraphContext), shell infrastructure (MF paths, auto-discovery, ui:register), and SDK sync (Python/TypeScript client updates).
Docstring Coverage ✅ Passed Docstring coverage is 95.45% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/billing-3

@kwit75 kwit75 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the infra / MF / model-server / build slices (my lane) — those are solid. Flagging the billing/auth/canvas UI for Ryan.

Infra / MF / model-server — LGTM

  • transformers.py: the slow-tokenizer (use_fast=False) fallback before AutoProcessor is the right fix for SentencePiece/Tiktoken models. 👍
  • MF /shell/apps/ → /apps/ split: consistent end-to-end — the new apps_static handler keeps the _resolve_safe path-traversal guard, and the __init__.py route, registerApp.js icon/readme URLs, and the rsbuild dev-server all moved to /apps/. Confirmed no stale /shell/apps refs left on the branch. The [shell-ui] MF remotes registered logging is a nice touch for debugging remote loading.

Two non-blockers to confirm

  1. constants.py: the scale constants were renamed + re-semantified (queue-depth → drain-time). Clean (no dangling old refs), but the new CONST_SCALE_*_DRAIN_TIME_S constants aren't referenced anywhere in-repo yet — is the replica-manager consumer landing in a follow-up, or is this dead config?
  2. registerApp.js: dropping the build-time stripeProductId check for subscription/paywall apps is fine if seed_apps → ensure_stripe_billing reliably sets it at runtime — just flagging the lost build-time guardrail.

Billing/checkout (PlanPicker/CheckoutModal), the subscription gate, the Zitadel auth flow, and the AJV canvas-validation overhaul are Ryan's domain — deferring those to him.

Note: CI's red on the Ubuntu build, and this landed after tonight's release cutoff, so from my side it's an AM / next-cut candidate, not tonight's prerelease.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/shell-ui/src/connection/connection.ts (1)

344-427: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider reducing production console logging.

The bootstrap and finishConnect flows now include extensive console.log statements at every decision point. While helpful for debugging complex OAuth flows, this may clutter production consoles.

Consider:

  • Moving verbose logs behind a debug flag (e.g., if (DEBUG) console.log(...))
  • Using a structured logger with configurable levels
  • Reducing to error-only logging in production builds

Also applies to: 436-482

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/shell-ui/src/connection/connection.ts` around lines 344 - 427, The code
emits verbose console.log calls throughout the bootstrap (and related
finishConnect) flow; replace these ad-hoc prints with a conditional debug
logger: add or reuse a DEBUG boolean or a Logger instance and change all
console.log(...) in the bootstrap method (and finishConnect) to use if (DEBUG)
logger.debug(...) or logger.debug(...) guarded by the flag, and keep only
console.error or logger.error for real errors (e.g., inside catch blocks around
client.login, OAuth exchange, token failures); reference the bootstrap method,
finishConnect, and functions like startOAuth, loadToken, clearToken,
client.login and ensure the new logger/flag is configurable so production builds
can disable verbose output.
packages/client-typescript/src/client/constants.ts (1)

55-60: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid https on port 80 for the SDK default cloud endpoint.

This default can cause failed connections for users who rely on implicit SDK configuration. Prefer https://api.rocketride.ai (or :443) for a reliable HTTPS/WSS default.

Suggested fix
-export const CONST_DEFAULT_WEB_CLOUD = 'https://api.rocketride.ai:80';
+export const CONST_DEFAULT_WEB_CLOUD = 'https://api.rocketride.ai';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/client-typescript/src/client/constants.ts` around lines 55 - 60, The
default cloud URL CONST_DEFAULT_WEB_CLOUD currently uses https on port 80 which
is incorrect; update CONST_DEFAULT_WEB_CLOUD to use the proper HTTPS host (e.g.
"https://api.rocketride.ai" or "https://api.rocketride.ai:443") and ensure the
deprecated alias CONST_DEFAULT_SERVICE continues to point to the updated
CONST_DEFAULT_WEB_CLOUD value so both constants reference the correct HTTPS
endpoint.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/chat-ui/scripts/tasks.js`:
- Line 98: The task "chat-ui:register" is a no-op; replace the stubbed action
with a call to registerApp so it actually writes apps.json: import or require
registerApp from scripts/lib/registerApp.js (if not already), then change the
task definition for 'chat-ui:register' from action: () => ({ run: async () => {}
}) to action: () => registerApp(APP_ROOT) (using the existing APP_ROOT constant
in this module), so the registerApp(APP_ROOT) function is invoked when the task
runs.

In `@apps/vscode/rsbuild.config.mjs`:
- Line 47: The VSCode build injects a ROCKETRIDE_URI default without the port
causing mismatch with SDKs; update the fallback used in rsbuild.config.mjs (the
'process.env.ROCKETRIDE_URI' entry) and the fallback in
apps/vscode/src/config.ts to use "https://api.rocketride.ai:80" so the WebSocket
origin matches the SDKs (alternatively, adjust the normalization in
packages/client-typescript/src/client/constants.ts and
packages/client-python/src/rocketride/core/constants.py to canonicalize
"https://api.rocketride.ai" to include ":80" for consistent wss:// endpoints
across clients).

In `@packages/ai/src/ai/common/models/transformers/transformers.py`:
- Around line 244-257: The three except branches in the tokenizer/processor
loading logic (the AutoTokenizer.from_pretrained fast-tokenizer except, the
fallback AutoTokenizer.from_pretrained(..., use_fast=False) except, and the
AutoProcessor.from_pretrained except that returns None) currently swallow
exceptions; update each catch to log a diagnostic error including model_name and
the caught exception/traceback (use the module's logger or Python logging) so
failures when trying AutoTokenizer (fast), the slow tokenizer fallback, or
AutoProcessor are recorded before falling through or returning None; keep the
existing control flow but ensure each except block logs a clear message
identifying the symbol AutoTokenizer/AutoProcessor and the exception details.

In `@packages/ai/src/ai/constants.py`:
- Around line 116-120: The autoscaling/replica-manager code still uses the
removed CONST_MODEL_* names and isn’t wired to the new constants; update the
scaling logic (e.g., in ReplicaManager, ReplicaManager.run()/_check_scaling(),
autoscale_replicas() or any ModelReplicaManager methods that perform drain-time
checks and scheduling) to import and use CONST_SCALE_UP_DRAIN_TIME_S,
CONST_SCALE_UP_DELAY_S, CONST_SCALE_DOWN_DRAIN_TIME_S, CONST_SCALE_DOWN_DELAY_S
for the drain-time thresholds and sustained-delay logic, and replace the
loop/check interval with CONST_REPLICA_MANAGER_INTERVAL_S; ensure per-model
state (timestamps/counters) uses the *_DELAY_S values when deciding to act and
that the periodic scheduler uses CONST_REPLICA_MANAGER_INTERVAL_S.

In `@packages/client-python/src/rocketride/core/constants.py`:
- Around line 66-69: The default cloud URL CONST_DEFAULT_WEB_CLOUD currently
uses an explicit nonstandard port ("https://api.rocketride.ai:80"); change it to
a proper TLS endpoint (e.g. "https://api.rocketride.ai" or
"https://api.rocketride.ai:443") and ensure CONST_DEFAULT_SERVICE (which aliases
CONST_DEFAULT_WEB_CLOUD) reflects the updated value so clients use the standard
HTTPS port by default.

In `@packages/shared-ui/src/components/sidebar-footer/SidebarFooter.tsx`:
- Around line 383-387: The two inline navigation buttons in SidebarFooter (the
buttons that call setTickerFade and setTickerIndex and reference announcements)
need explicit type and accessibility labels: add type="button" to each button to
prevent accidental form submission, and add descriptive aria-label attributes
(e.g., "Previous announcement" for the left button and "Next announcement" for
the right button) so screen readers can announce their purpose; leave the
existing onClick handlers and styles unchanged.
- Around line 362-392: The external announcement link (current.link) is used
directly in an <a href> which allows unsafe schemes like javascript:; add a
URL-sanitizer helper (e.g., safeHref(url: string|undefined): string|undefined)
that parses the string with the URL constructor and only returns it when
protocol is http: or https:, and update the render in SidebarFooter (where
current.link is referenced) to pass current.link through safeHref and only
render the anchor when the sanitized value exists; this ensures malicious
protocols are rejected and preserves existing behavior when links are absent.

In `@packages/shared-ui/src/hooks/useAnnouncements.ts`:
- Around line 96-134: The fetchAnnouncements function can hang because
fetch(FETCH_URL) has no timeout; wrap the fetch in an AbortController with a
short configurable timeout (e.g. FETCH_TIMEOUT_MS) and pass controller.signal
into fetch(FETCH_URL, { signal }), start a setTimeout to call controller.abort()
when the timeout elapses, and clear that timer in the finally block to avoid
leaks; ensure fetchPromise, cachedAnnouncements, cacheTimestamp and
resolveImagePaths usages remain the same and handle aborted fetches in the
existing catch (return cachedAnnouncements) so deduplication and cleanup still
work.

In `@packages/shared-ui/src/modules/checkout/PlanPicker.tsx`:
- Around line 242-248: The effect in PlanPicker.tsx only sets interval to 'year'
when month plans are missing, leaving a stale 'year' when new plans include
months; update the useEffect that reads plans to be symmetric: if plans contain
a month interval call setInterval('month'), else if they contain a year interval
call setInterval('year'). Locate the useEffect that checks plans.some((p) =>
p.interval === 'month') and replace the logic so it sets to 'month' when months
exist, otherwise falls back to 'year' when only years are present (use the
existing setInterval state updater).
- Around line 272-277: The visiblePlans filter and any other comparisons in
PlanPicker.tsx are checking for the non-existent 'one_time' interval, causing
dead comparisons and type issues; remove the redundant "=== 'one_time'" checks
and rely on the existing falsy checks (!p.interval / !plan.interval) which cover
the '' case, updating all occurrences where p.interval === 'one_time' or
plan.interval === 'one_time' in the useMemo visiblePlans logic (and similar
filters) so they only test !p.interval or compare to 'month'/'year' as needed to
satisfy CheckoutPlan.interval's type.

In `@packages/shared-ui/src/modules/checkout/types.ts`:
- Around line 56-57: The interval type on the billing Plan interface (interval:
'month' | 'year' | '') is missing the 'one_time' variant that PlanPicker.tsx
checks; update the union for the interval property to include 'one_time' (i.e.,
'month' | 'year' | 'one_time' | '') so PlanPicker comparisons (in
PlanPicker.tsx) type-check correctly, or alternatively remove the 'one_time'
comparisons from PlanPicker if that variant is not needed—make the change in the
interval declaration to keep consumers and the type consistent.

---

Outside diff comments:
In `@apps/shell-ui/src/connection/connection.ts`:
- Around line 344-427: The code emits verbose console.log calls throughout the
bootstrap (and related finishConnect) flow; replace these ad-hoc prints with a
conditional debug logger: add or reuse a DEBUG boolean or a Logger instance and
change all console.log(...) in the bootstrap method (and finishConnect) to use
if (DEBUG) logger.debug(...) or logger.debug(...) guarded by the flag, and keep
only console.error or logger.error for real errors (e.g., inside catch blocks
around client.login, OAuth exchange, token failures); reference the bootstrap
method, finishConnect, and functions like startOAuth, loadToken, clearToken,
client.login and ensure the new logger/flag is configurable so production builds
can disable verbose output.

In `@packages/client-typescript/src/client/constants.ts`:
- Around line 55-60: The default cloud URL CONST_DEFAULT_WEB_CLOUD currently
uses https on port 80 which is incorrect; update CONST_DEFAULT_WEB_CLOUD to use
the proper HTTPS host (e.g. "https://api.rocketride.ai" or
"https://api.rocketride.ai:443") and ensure the deprecated alias
CONST_DEFAULT_SERVICE continues to point to the updated CONST_DEFAULT_WEB_CLOUD
value so both constants reference the correct HTTPS endpoint.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0c9d9a78-a85d-42ed-ae59-f4c87c8484f0

📥 Commits

Reviewing files that changed from the base of the PR and between c3b7720 and 43e76da.

📒 Files selected for processing (52)
  • apps/chat-ui/scripts/tasks.js
  • apps/dropper-ui/scripts/tasks.js
  • apps/shell-ui/rsbuild.config.mts
  • apps/shell-ui/src/auth/CloudAuthProvider.ts
  • apps/shell-ui/src/bootstrap.tsx
  • apps/shell-ui/src/components/layout/Shell.tsx
  • apps/shell-ui/src/components/layout/ShellLayout.tsx
  • apps/shell-ui/src/components/layout/Sidebar.tsx
  • apps/shell-ui/src/connection/connection.ts
  • apps/shell-ui/src/lib/Documents.tsx
  • apps/shell-ui/src/lib/appLoader.ts
  • apps/shell-ui/src/workspace/useWorkspaceState.ts
  • apps/vscode/rsbuild.config.mjs
  • apps/vscode/src/config.ts
  • apps/vscode/src/providers/ProjectProvider.ts
  • apps/vscode/src/providers/template.html
  • docs/README-python-client.md
  • docs/README-typescript-client.md
  • docs/agents/ROCKETRIDE_PIPELINE_RULES.md
  • docs/agents/ROCKETRIDE_QUICKSTART.md
  • docs/agents/ROCKETRIDE_python_API.md
  • docs/agents/ROCKETRIDE_typescript_API.md
  • packages/ai/src/ai/common/models/transformers/transformers.py
  • packages/ai/src/ai/constants.py
  • packages/ai/src/ai/modules/shell/__init__.py
  • packages/ai/src/ai/modules/shell/shell.py
  • packages/client-python/src/rocketride/core/constants.py
  • packages/client-python/src/rocketride/types/__init__.py
  • packages/client-python/src/rocketride/types/billing.py
  • packages/client-python/tests/RocketRideClient_test.py
  • packages/client-typescript/src/cli/rocketride.ts
  • packages/client-typescript/src/client/client.ts
  • packages/client-typescript/src/client/constants.ts
  • packages/client-typescript/src/client/types/billing.ts
  • packages/client-typescript/src/client/types/client.ts
  • packages/client-typescript/tests/RocketRideClient.test.ts
  • packages/shared-ui/src/components/canvas/components/FlowCanvas.tsx
  • packages/shared-ui/src/components/canvas/components/panels/create-node/CreateNodePanel.tsx
  • packages/shared-ui/src/components/canvas/components/panels/node-config/NodeConfigPanel.tsx
  • packages/shared-ui/src/components/canvas/context/FlowGraphContext.tsx
  • packages/shared-ui/src/components/canvas/util/csp-safe-validator.ts
  • packages/shared-ui/src/components/canvas/util/rjsf.ts
  • packages/shared-ui/src/components/sidebar-footer/SidebarFooter.tsx
  • packages/shared-ui/src/hooks/useAnnouncements.ts
  • packages/shared-ui/src/index.ts
  • packages/shared-ui/src/modules/checkout/CheckoutModal.tsx
  • packages/shared-ui/src/modules/checkout/PlanPicker.tsx
  • packages/shared-ui/src/modules/checkout/index.ts
  • packages/shared-ui/src/modules/checkout/types.ts
  • scripts/lib/appModule.js
  • scripts/lib/registerApp.js
  • scripts/tasks.js
💤 Files with no reviewable changes (1)
  • packages/shared-ui/src/components/canvas/util/csp-safe-validator.ts

Comment thread apps/chat-ui/scripts/tasks.js
Comment thread apps/vscode/rsbuild.config.mjs
Comment thread packages/ai/src/ai/common/models/transformers/transformers.py
Comment thread packages/ai/src/ai/constants.py
Comment thread packages/client-python/src/rocketride/core/constants.py Outdated
Comment thread packages/shared-ui/src/components/sidebar-footer/SidebarFooter.tsx
Comment thread packages/shared-ui/src/hooks/useAnnouncements.ts
Comment thread packages/shared-ui/src/modules/checkout/PlanPicker.tsx
Comment thread packages/shared-ui/src/modules/checkout/PlanPicker.tsx
Comment thread packages/shared-ui/src/modules/checkout/types.ts Outdated

@asclearuc asclearuc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@Rod-Christensen

Some inline comments from CodeRabbit and me.

Comment thread apps/shell-ui/src/components/layout/Shell.tsx Outdated
Comment thread packages/client-python/src/rocketride/core/constants.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/shared-ui/src/modules/checkout/PlanPicker.tsx (1)

276-278: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't hide action cards behind the interval toggle.

The component docs say action plans are always visible, but this filter still drops any action plan whose interval is 'month' or 'year' when the other tab is selected. That makes Free / Enterprise CTAs disappear as soon as pricing metadata includes an interval.

💡 Minimal fix
 	const visiblePlans = useMemo(() => {
 		const filtered = showToggle
-			? plans.filter((p) => !p.interval || p.interval === 'one_time' || p.interval === interval)
+			? plans.filter(
+					(p) => !!p.action || !p.interval || p.interval === 'one_time' || p.interval === interval
+				)
 			: plans;
 		return [...filtered].sort((a, b) => (a.order ?? 500) - (b.order ?? 500));
 	}, [plans, interval, showToggle]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/shared-ui/src/modules/checkout/PlanPicker.tsx` around lines 276 -
278, The current filter in PlanPicker.tsx (the filtered assignment using
showToggle, plans, interval) hides action plans when their interval doesn't
match; change the predicate so action plans are always included: in the filter
for variable filtered, add a clause that returns true when p.type === 'action'
(or the plan's action flag if named differently) so that action plans bypass the
interval check and remain visible regardless of selected interval.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/shared-ui/src/modules/checkout/types.ts`:
- Around line 56-57: CheckoutPlan.interval was extended to include 'one_time'
but the SDK type StripePlan.interval (in client-typescript's billing types)
still omits 'one_time', causing type mismatch; update the exported
StripePlan.interval union to include 'one_time' (mirror CheckoutPlan.interval:
'month' | 'year' | 'one_time' | '') so SDK consumers can pass one-time plans
into CheckoutModal without casting and keep both packages' billing types in
sync.

---

Outside diff comments:
In `@packages/shared-ui/src/modules/checkout/PlanPicker.tsx`:
- Around line 276-278: The current filter in PlanPicker.tsx (the filtered
assignment using showToggle, plans, interval) hides action plans when their
interval doesn't match; change the predicate so action plans are always
included: in the filter for variable filtered, add a clause that returns true
when p.type === 'action' (or the plan's action flag if named differently) so
that action plans bypass the interval check and remain visible regardless of
selected interval.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9f9699bc-944d-4c59-9efa-ddce328fb2d5

📥 Commits

Reviewing files that changed from the base of the PR and between 43e76da and 1a00242.

📒 Files selected for processing (10)
  • apps/chat-ui/scripts/tasks.js
  • apps/dropper-ui/scripts/tasks.js
  • apps/shell-ui/src/components/layout/Shell.tsx
  • packages/ai/src/ai/common/models/transformers/transformers.py
  • packages/client-python/src/rocketride/core/constants.py
  • packages/client-typescript/src/client/constants.ts
  • packages/shared-ui/src/components/sidebar-footer/SidebarFooter.tsx
  • packages/shared-ui/src/hooks/useAnnouncements.ts
  • packages/shared-ui/src/modules/checkout/PlanPicker.tsx
  • packages/shared-ui/src/modules/checkout/types.ts
💤 Files with no reviewable changes (1)
  • apps/shell-ui/src/components/layout/Shell.tsx

Comment thread packages/shared-ui/src/modules/checkout/types.ts
Rod-Christensen and others added 13 commits June 4, 2026 11:25
…otstrap logging

Cloud URI:
- Remove .config file; all build-time env vars now come from .env
- VSCode cloud mode: ignore stale hostUrl from settings, use build-time
  ROCKETRIDE_URI or default to https://api.rocketride.ai
- rsbuild: default ROCKETRIDE_URI to https://api.rocketride.ai so the
  CloudPanel probe fires correctly on first boot
- Update docs and SDK references from cloud.rocketride.ai to api.rocketride.ai

Replica Manager:
- Restore drain-time scaling constants (CONST_SCALE_UP_DRAIN_TIME_S etc.)
  that were renamed to queue-depth constants in 51f3b0d but never consumed;
  replica_manager.py still imports the old names and would crash on import

Bootstrap debugging:
- Add console.log tracing to shell-ui bootstrap and finishConnect to
  diagnose first-login hang on cloud.rocketride.ai

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hardcoded app lists in ui:build and ui:clean with dynamic
registry lookup — any registered *-ui module (OSS or overlay) is
picked up automatically without maintaining a manual list.

Add ui:register aggregate task that writes all app manifest entries
into apps.json without bundling. Used by the SaaS engine CI to
generate the complete app catalog for DB seeding without the cost
of a full UI build.

Add no-op :register actions to chat-ui and dropper-ui (hand-written
modules that don't use createAppModule and have no apps.json entry).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a console.log in registerAndMapApps() that prints each Module
Federation remote's moduleId and entry URL right after registerRemotes()
is called. This makes it easy to verify which app bundles the shell is
wiring up and where they resolve to.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The /shell/apps/ path for Module Federation remote bundles created
confusion and bugs: the shell dev server's publicDir pointed at
dist/server/static/ expecting bundles at static/shell/apps/, but the
builder output them to build/apps/. The extra /shell/ nesting also
forced the CI workflow to maintain duplicate copy targets.

This commit gives app bundles their own top-level /apps/ route,
separating them from the shell's own assets (/shell/ for JS/CSS/themes).

Build system:
- registerApp.js: entry, icon, and readme URLs now use /${APPS_BASE}/
  instead of /shell/${APPS_BASE}/ (e.g. /apps/home-ui/remoteEntry.js)
- appModule.js: copy destination changed from
  dist/server/static/shell/apps/<name> to dist/server/static/apps/<name>

Server:
- shell.py: added _apps_root (static/apps/) and new apps_static()
  handler that serves MF bundles with path-traversal protection and
  404 responses (no SPA fallback — these are JS/CSS assets, not routes)
- shell/__init__.py: registered GET /apps/{file_path:path} route
  alongside the existing /shell/ route

Shell host (dev):
- rsbuild.config.mts: dev publicDir now serves from build/ (via
  ROCKETRIDE_BUILD_ROOT) instead of dist/server/static/, so /apps/*
  resolves directly to the builder output at build/apps/*
- bootstrap.tsx: import Shell and AppManifestEntry from relative paths
  instead of bare 'shell-ui' — MF intercepts bare imports and tries to
  load from the share scope, but the host hasn't registered its own
  factory yet, causing an undefined factory error (RUNTIME-006)

Types:
- client.ts: updated AppManifestEntry.icon docstring example path

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The .config file was accidentally removed in a previous commit. Without
it, getenv() returns empty strings for RR_ZITADEL_URL, RR_ZITADEL_CLIENT_ID,
and RR_STRIPE_PUBLISHABLE_KEY unless the developer has a local .env that
sets them — causing "Zitadel not configured" in shell-ui and breaking
OAuth login.

This file provides safe-to-commit public defaults (OAuth client IDs,
Zitadel issuer URL, Stripe publishable key, localhost server URI) that
getenv() loads first, with .env layered on top as overrides. CI also
relies on .config for the base values before writing its own .env.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After auth, Shell.tsx called registerAndMapApps() with only the user's
desktop apps (ConnectResult.apps), which invoked registerRemotes() with
force:true — wiping the full probe catalog. Apps like home-ui that were
registered from the probe but not in the user's desktop list became
unloadable, causing the shell to hang on "Loading...".

MF remote registration (moduleId → remoteEntry URL) only needs to
happen once from the probe. Post-auth, the URLs don't change — the
shell only needs to overlay desktop metadata (appStatus, onDesktop)
onto the existing probe entries for UI display purposes.

- Remove registerAndMapApps import from Shell.tsx (no longer used)
- Replace the apps useMemo with a simple metadata overlay that merges
  desktop fields onto probe entries without touching MF registration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s + fix workspace seed state

Tokenizer: AutoTokenizer.from_pretrained() tries fast (Rust) tokenizer
first; models like Davlan/xlm-roberta-base-ner-hrl use SentencePiece
which can't auto-convert to Rust, causing persistent load failures in
chaos tests. Now retries with use_fast=False before falling back to
AutoProcessor.

Workspace: set seeded=true on all load paths so downstream consumers
don't block waiting for seed completion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously the ShellLayout only had an auth gate — it checked whether
the user was logged in before rendering an app that requires
authentication.  However there was no equivalent check for subscription
status.  This meant a logged-in user could navigate directly to a
subscription-based app (e.g. Brandy) and see the full app UI even
without an active subscription.

Add a subscription gate that mirrors the auth gate pattern:

1. Compute `subGateActive` — true when the user IS authenticated but
   the active app's `appStatus` is 'unsubscribed' (and it's not the
   default/home app).

2. An effect emits `shell:subscribe` with the full manifest entry,
   which triggers the existing CheckoutFlow to open the Stripe
   checkout modal automatically.

3. A `subGateTriggeredRef` prevents re-emitting for the same app
   (same pattern as `authGateTriggeredRef`).

4. The client area renders a "Subscription required" placeholder
   instead of loading the app component while the gate is active.

Once the Stripe webhook fires after successful checkout, the server
pushes an updated identity with `appStatus: 'subscribed'`, which
propagates through Shell → WorkspaceContext → ShellLayout, clearing
`subGateActive` and allowing the app to render.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ts, and checkout improvements

This commit consolidates several billing and UI improvements across
shell-ui, shared-ui, and client-typescript.

## Shared PlanPicker component (shared-ui)

Extract the plan card picker from CheckoutModal into a standalone
PlanPicker component so it can be reused by both the checkout flow
and the public pricing page.

- New PlanPicker.tsx: interval toggle (month/year), plan card grid,
  action plan support (link/mailto CTA buttons vs selectable cards),
  auto-selection when toggling intervals.
- CheckoutModal simplified to a two-step flow: PlanPicker → PaymentForm.
  All plan card rendering and interval logic moved to PlanPicker.
- New CheckoutPlan.action and PlanAction types in checkout/types.ts
  to support non-checkout plan cards (Free tier → GitHub link,
  Enterprise → mailto).
- Matching PlanAction and StripePlan types added to client-typescript
  billing types so the SDK can return rich plan metadata (description
  lines, display amounts, action, order, credit grants).
- Exports added to shared-ui barrel (PlanPicker, PlanPickerProps,
  PlanAction, useAnnouncements, Announcement).

## Auth provider delegation (shell-ui)

Refactor ConnectionManager to accept an IAuthProvider interface
instead of managing PKCE OAuth internally:

- Shell.tsx initializes CloudAuthProvider and passes it to cm.init().
- ConnectionManager.startOAuth() delegates to authProvider.signIn()
  when available, with legacy PKCE fallback.
- CloudAuthProvider.signIn() uses location.replace() instead of
  location.href so the browser back button skips expired Zitadel
  authRequestID pages.
- Legacy zitadelUrl/zitadelClientId options marked @deprecated.

## Announcements (shared-ui)

- New useAnnouncements hook: fetches announcements from the
  rocketride-org/announcements GitHub repo, caches for 1 hour,
  filters by valid_from/valid_until dates.
- SidebarFooter updated to use the hook instead of hardcoded
  announcements array. Renders markdown in titles/bodies. Adds
  prev/next navigation buttons when multiple announcements exist.
  Gracefully hides the ticker when no announcements are available.

## Other changes

- Sidebar: collapsed state shows the same resolved app icon instead
  of always showing the RocketRide mark.
- registerApp.js: removed build-time stripeProductId validation
  (Stripe products are now created at runtime via seed_apps).
  Added billing section passthrough to apps.json for seed_apps to
  provision Stripe products.
- tasks.js: exclude shared-ui from remote UI module list (it's a
  library, not a deployable remote).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n, add drag-through-backdrop

VALIDATION OVERHAUL:
- Delete csp-safe-validator.ts (cfworker/json-schema wrapper) entirely.
  The cfworker validator silently crashed on the engine's non-standard
  "object" key in generated JSON schemas, causing formDataValid to
  always return true — meaning nodes with missing API keys never showed
  the red gear icon or validation snackbar.
- Switch both rjsf.ts and NodeConfigPanel.tsx to @rjsf/validator-ajv8,
  which tolerates unknown schema keywords and correctly handles
  dependencies/oneOf resolution.
- Add recursive stripEmptyStrings() to rjsf.ts so that empty string
  values ("") are removed before validation — JSON Schema's "required"
  only checks key presence, so "" was passing validation for fields
  like apikey that must be filled in.
- Add 'unsafe-eval' to CSP in both template.html and ProjectProvider.ts
  because AJV compiles schemas via new Function() at runtime.

RE-VALIDATION ON ASYNC SERVICE CATALOG LOAD:
- Move configSnackbar state and auto-hide timer from FlowCanvas into
  FlowGraphContext so both loadData and the servicesJson effect can
  set the snackbar message.
- loadData now re-validates every node's formDataValid against current
  schemas on project load and returns the unconfigured count.
- Add useEffect([servicesJson]) in FlowGraphContext that re-validates
  all nodes when the service catalog arrives asynchronously. This is
  necessary because servicesJson is empty when loadData first runs on
  canvas mount — without this effect, nodes loaded before the catalog
  arrives would never get validated.

CREATE-NODE PANEL DRAG FIX (PR #1076):
- Forward onDragOver and onDrop through the CreateNodePanel backdrop
  div so that dragging a node from the panel onto the canvas works
  even when the backdrop overlay intercepts the drop event.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The TypeScript SDK's billing.ts was updated with new fields during the
billing/checkout refactor but the Python SDK was not kept in sync.

- Add PlanAction TypedDict (type, url, subject, label) for action plans
  (Free → GitHub link, Enterprise → mailto)
- Expand StripePlan with label, amount, cents, currency, description,
  action, order, credits, and labels fields to match the TS interface
- Update StripePlan.interval to include '' for non-recurring plans
- Add credits and creditLabels to BillingDetail
- Add labels to CreditBalance
- Export PlanAction from types/__init__.py and __all__

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- chat-ui/dropper-ui: document register tasks as intentional no-ops
  (standalone pages, not shell-ui MF remotes)
- SDK constants: drop :80 from HTTPS cloud URI — port 80 is HTTP default,
  not HTTPS; aligns VSCode, TS SDK, and Python SDK on same endpoint
- SidebarFooter: sanitize announcement link URLs — only allow http(s)://
  schemes to prevent javascript: injection from remote JSON; add
  target="_blank" rel="noopener noreferrer"; add type="button" and
  aria-label to prev/next announcement buttons
- useAnnouncements: add 10s AbortSignal timeout to fetch
- PlanPicker: reset interval back to 'month' when plans change and month
  plans are available (was only switching to 'year', never back)
- types.ts: add 'one_time' to CheckoutPlan.interval union — PlanPicker
  already references it in the filter logic
- transformers.py: add rocketlib debug() on tokenizer/processor fallback
  paths so load failures are traceable
- Shell.tsx: remove debug console.log statements left from development

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both TS and Python SDK StripePlan types now include 'one_time' in the
interval union, matching CheckoutPlan in shared-ui. Without this, SDK
consumers can't pass one-time plans to CheckoutModal without a cast.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Rod-Christensen
Rod-Christensen merged commit 7ce7961 into develop Jun 4, 2026
16 of 17 checks passed
@Rod-Christensen
Rod-Christensen deleted the feat/billing-3 branch June 4, 2026 18:27
dylan-savage added a commit that referenced this pull request Aug 17, 2026
Twelve of thirteen findings accepted; several were symptoms of wider problems
and were fixed at the root.

Wrong technical claims, verified against the source and rewritten:
- Data lanes and agent tools were described as mutually exclusive. Nine of
  27 `tool` services declare lanes, and validate-node-readme.py gates the two
  sections independently. Replaced with a 2x2 matrix.
- The node loader contract was pseudocode for a `MyNode`/`process()` API that
  does not exist. python-instance.cpp requires `IInstance`, python-global.cpp
  treats `IGlobal` as optional, and bindMethod binds callbacks by name from a
  24-entry list, silently no-oping on a typo. Replaced with a working
  three-file template.
- `./builder test server` is not an alias for the server tests: bare args fan
  out to every module. Corrected here and two more sites in index.md.

Host and path corrections:
- The SDK `uri` examples used cloud.rocketride.ai, which is the web console.
  The API host is api.rocketride.ai (CONST_DEFAULT_WEB_CLOUD in both SDKs
  since #1078). Only SDK/URI contexts were changed; ~20 sites across the
  Python, TypeScript and VS Code pages, which had drifted away from the
  package READMEs that were already correct.
- The exported agent doc linked into docs/development/, which docs:export
  never copies to .rocketride/docs/. Repointed at a sibling exported doc and
  published URLs.
- docs.rocketride.ai -> .org in apps/index.md, matching docusaurus.config.ts.

Also: documented the four app-SDK methods missing from the API list, corrected
the n8n trigger's authentication description (it declares no credentials, only
a `secret` parameter checked against the Authorization header), fixed rsbuild
dependency drift in the app template, normalized bare `builder` to `./builder`
in 14 places, tagged three untagged code fences, and ran prettier over
redirects.ts (all 702 entry lines predated this PR; array data verified
identical before and after).

Rejected: the suggestion to unify updateEditorViewState and
updateEditorViewport. They are distinct methods with different signatures in
both Documents.tsx and shell-api/v0.d.ts; unifying them would introduce a bug.
Also rejected `npx` -> `pnpm exec`, which the apps' own tasks.js contradict.

Two source-level issues found and left for separate PRs: app-sdk/index.ts is
missing four methods the runtime exposes, and tool_firecrawl/README.md was
already schema-nonconformant.
dylan-savage added a commit that referenced this pull request Aug 17, 2026
…ges (#1969)

* refactor(docs): reorganize docs/ by audience and retire duplicated pages

Collapses docs/ to three top-level folders keyed by who reads them, brings all
five clients under one roof, and removes the hand-written pages that duplicated
generated node documentation.

docs/
  public/       humans outside the repo: typescript, python, vscode, mcp, n8n
                (each with a distribution README.md and assets/), product/ (the
                site spine), assets/
  development/  contributors; never published, no exceptions
  agents/       AI assistants: ROCKETRIDE_*.md, stubs/, skills/

clients/, protocols/, stubs/ and images/ are dissolved. Site mount destinations
are unchanged, so no published URL moves except where noted below.

Five clients, one place. mcp graduates from protocols/ to a client folder (its
README already was the published rocketride-mcp README). n8n joins as the fifth:
packages/n8n-nodes/README.md is now generated by docs:export like the other
three, so it can no longer drift.

Integration pages retired. Six of seven pages under product/integrations were
node-README duplicates at 73-90% overlap, and postgres.md had already drifted
into publishing Supabase instructions that the db_postgres README says will fail
to resolve. All six are deleted and redirected to their node pages; n8n survives
because it documents webhook ingress, credential-vault and human-in-the-loop
flows that no single node README can own.

development/ is now genuinely internal. Its websocket docs split by audience:
the wire-protocol reference (external integrators) moved to the site spine and
keeps its URLs, while crash-reporting.md (lldb, breakpad) is unpublished. apps.md
split likewise, its published-SDK half becoming develop/apps with shell-ui
imports normalized to rocketride/app-sdk. gather no longer sweeps development/
at all, so it cannot publish by accident. The remainder is grouped by subsystem
(builder/, engine/, nodes/, clients/, apps/).

product/ regrouped. The real quickstart was filed under Evaluate while
/quickstart was an empty card grid pointing at it; it is now a landing page with
the tutorial split into an IDE walkthrough and an SDK guide. Loose root files
became operate/ (cloud, self-hosting, troubleshooting) and reference/ (cli,
glossary, pipeline-reference).

Broken distribution assets fixed. Every banner in the published READMEs pointed
at a main-pinned raw URL for a path that had never existed there, so all of them
404 on npm, PyPI and the Marketplace. They now target the per-client assets/
paths; the root README uses relative paths that resolve on any branch.

New guardrail: a doc id is a public URL here, and a spine id with no backing file
used to publish a silent "coming soon" page. assertNoUnexpectedPlaceholders now
fails the build and names the offending id, with an allowlist for intentional
stubs. It caught four moved URLs hardcoded in the site footer during this work.

Also adds development/docs-pipeline.md and development/ci-gates.md, deletes a
service catalog that had drifted to 118/148 against a real 143/163, and fixes
setup-guide and root-file link depths.

Verified: docs:build 176 pages / 125 nodes / 5 mounts, docs:check in sync,
docs:test 48/48, validate-client-docs.py passing, docs:export idempotent. 14
retired routes all have emitted redirects.

* docs: address CodeRabbit review on #1969

Twelve of thirteen findings accepted; several were symptoms of wider problems
and were fixed at the root.

Wrong technical claims, verified against the source and rewritten:
- Data lanes and agent tools were described as mutually exclusive. Nine of
  27 `tool` services declare lanes, and validate-node-readme.py gates the two
  sections independently. Replaced with a 2x2 matrix.
- The node loader contract was pseudocode for a `MyNode`/`process()` API that
  does not exist. python-instance.cpp requires `IInstance`, python-global.cpp
  treats `IGlobal` as optional, and bindMethod binds callbacks by name from a
  24-entry list, silently no-oping on a typo. Replaced with a working
  three-file template.
- `./builder test server` is not an alias for the server tests: bare args fan
  out to every module. Corrected here and two more sites in index.md.

Host and path corrections:
- The SDK `uri` examples used cloud.rocketride.ai, which is the web console.
  The API host is api.rocketride.ai (CONST_DEFAULT_WEB_CLOUD in both SDKs
  since #1078). Only SDK/URI contexts were changed; ~20 sites across the
  Python, TypeScript and VS Code pages, which had drifted away from the
  package READMEs that were already correct.
- The exported agent doc linked into docs/development/, which docs:export
  never copies to .rocketride/docs/. Repointed at a sibling exported doc and
  published URLs.
- docs.rocketride.ai -> .org in apps/index.md, matching docusaurus.config.ts.

Also: documented the four app-SDK methods missing from the API list, corrected
the n8n trigger's authentication description (it declares no credentials, only
a `secret` parameter checked against the Authorization header), fixed rsbuild
dependency drift in the app template, normalized bare `builder` to `./builder`
in 14 places, tagged three untagged code fences, and ran prettier over
redirects.ts (all 702 entry lines predated this PR; array data verified
identical before and after).

Rejected: the suggestion to unify updateEditorViewState and
updateEditorViewport. They are distinct methods with different signatures in
both Documents.tsx and shell-api/v0.d.ts; unifying them would introduce a bug.
Also rejected `npx` -> `pnpm exec`, which the apps' own tasks.js contradict.

Two source-level issues found and left for separate PRs: app-sdk/index.ts is
missing four methods the runtime exposes, and tool_firecrawl/README.md was
already schema-nonconformant.
dylan-savage added a commit that referenced this pull request Sep 14, 2026
…+ CI gates, node migration, new site (#2139)

* feat(docs): consolidate hand-written docs into top-level docs/ with export pipeline (#1856)

* feat(docs): move spine pages to docs/product/, re-point gather

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

* feat(docs): mount client/mcp/engine docs from top-level docs/ tree

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

* feat(docs): move contributor docs to docs/internal/

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

* feat(docs): add docs:export/docs:check; readme sources under docs/

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

* fix(docs): docs:check skips gitignored export destinations

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

* fix(docs): commit exported package READMEs (un-gitignore)

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

* feat(docs): remove docs/images and all references

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

* fix(docs): restore list spacing in vscode docs

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

* docs: update doc-location rules, folder guide, CI drift gate

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

* ci(docs): trigger docs workflow on top-level docs/ changes

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

* fix(clients): source package READMEs from committed docs:export copies

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

* fix(docs): close CI drift-gate gap, restore .pipe rule bullet

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

* docs: drop reserved skills/ section from docs/ tree

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

* docs: move top-level images/ into docs/images/

All doc-referenced images now live under docs/. Root README relative
paths and main/develop-pinned raw.githubusercontent URLs rewritten;
generated package READMEs refreshed via docs:export. Orphans
(banner-root, screenshot-ide, screenshot-sdk) carried over unchanged.

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

* docs: fix stale README-* link labels and broken post-move links in internal docs

CodeRabbit bucket-1 findings plus three broken links the sweep surfaced:
README-vscode.md and README-*-client.md references now point at the new
clients/ and mcp/ locations.

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

* docs: address CodeRabbit review findings across doc sources

Verified each finding against current SDK/node code before fixing:
- H1s, fence languages, MD031 blank lines, heading levels (lint batch)
- Example correctness: import os, double-wrapped pipeline args (4 sites),
  undefined vars, Answer instance API documented from Question.ts,
  validate-gated use(), deploy rollback to recorded version,
  getTaskStatus/validate signatures from client.ts
- Config correctness: postgres questions-lane wiring (both examples),
  qdrant serverName->host, neo4j password field, execution-model
  documents lane, node count 100+
- Security-hardening in crash-reporting.md examples (pinned checksum
  install, mktemp, env-var instead of interpolation), self-hosting
  default host now 127.0.0.1
- builder.md skip conditions include --force; LICENSE link depth (2 files)
- Generated package READMEs refreshed via docs:export

Deferred as code-behavior (not doc) issues: firecrawl unbounded 429
retry, vscode ROCKETRIDE_APIKEY persistence on disconnect.

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

---------

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

* feat(docs): documentation schemas, validators, CI gates, and docs tree cleanup (#1912)

* docs(agents): make AGENTS.md the single-source contributor router

AGENTS.md becomes the one canonical guide: repo overview, task routing
table into docs/, common commands (absorbed from .claude/CLAUDE.md),
conventions, and the documentation rules. .claude/CLAUDE.md becomes an
@AGENTS.md import and .cursorrules a pointer, eliminating the three
divergent copies of the doc-location rules. A nested nodes/AGENTS.md
gives agents working in the nodes tree the README schema and validator
command with zero indirection. docs/README.md references the
documentation contracts in docs/internal/.

* docs(nodes): add node README schema and deterministic validator

docs/internal/node-readme-schema.md defines the per-node README contract:
a fixed section set where services*.json triggers decide which sections
are required (invoke→Connections, lanes→Lanes, tool∈classType→As a tool,
≥2 profiles→Profiles, gpu→Requirements, restricted capabilities→
Limitations), core sections for every node (summary, What it does,
Configuration usage guidance, Example pipelines), and optional sections
with placement rules (About vendor, Authentication, Notes, Upstream
docs). The generated ROCKETRIDE:GENERATED:PARAMS region owns the field
reference; hand-written Configuration is usage guidance only.

scripts/validate-node-readme.py enforces the structural rules per node:
section presence/order, forbidden-when-untriggered, table parity against
declared invoke keys/lanes/profiles, and warnings when a field with
objective complexity signals lacks a detail subsection. Multi-service
directories are merged; fragment-only files (no protocol) are skipped.

* docs(nodes): migrate six sample node READMEs to the README schema

llm_anthropic, db_postgres, tool_github, store_qdrant, ocr, and
agent_deepagent restructured to the schema in
docs/internal/node-readme-schema.md. Content-preserving: existing prose
kept, sections reordered into the canonical layout, bespoke headings
folded into their owning sections or Notes, and the generated
ROCKETRIDE:GENERATED:PARAMS regions untouched byte-for-byte.

Substantive fixes made along the way:
- llm_anthropic: Profiles table now lists all 21 declared profiles
  (previously 7 — Opus 5, Fable 5, Sonnet 5, 4.8 variants, latest
  aliases and 3 Haiku were declared in services.json but undocumented)
- tool_github: removed a duplicated all-tools table that shadowed the
  per-group tool tables; grouped tables are now the single source
- All six gain Example pipelines; db_postgres gains the Limitations
  section its noremote capability requires; vendor nodes gain About
  sections

Validator: Profiles parity accepts key-or-title in the first two
columns; grouped detail subsections satisfy the complex-field check via
token containment. Schema: preset services with identical wiring may be
documented once (db_postgres/Supabase case).

* ci(docs): advisory node README schema check + CodeRabbit doc rules

New node-docs CI job runs scripts/validate-node-readme.py over the node
tree on every PR. Advisory for now (continue-on-error, not in ci-ok):
the corpus is mid-migration to the schema, so the job surfaces results
without blocking merges; flipping it to a hard gate is a two-line change
once migration completes.

CodeRabbit path_instructions direct review attention where the
deterministic validator cannot reach: prose accuracy of node READMEs
against code/services*.json (structure is machine-checked), and
Python/TypeScript client-doc parity.

* docs(clients): client readme schema, parity checker, and parity fixes

docs/internal/client-readme-schema.md codifies the shared section order
the Python and TypeScript readmes already follow, fixes the insertion
points for language-specific sections, and states the parity rule: any
documented method/type/exception exists in both readmes (snake_case and
camelCase compare equal) or carries a language-specific marker.

scripts/validate-client-docs.py enforces it deterministically: section
presence/order/ownership plus API symbol parity harvested from method
tables across the six shared API sections.

Parity gaps it found, fixed against client source:
- python readme was missing the attach/detach/is_attached/login/logout/
  is_authenticated/set_env connection lifecycle (implemented in
  mixins/connection.py, previously undocumented) and Answer.setAnswer
- typescript readme was missing Question.addGoal (Question.ts:264)
- dap_request (python convenience) and withConnection (typescript
  convenience) marked language-specific with cross-references

Package READMEs regenerated via docs:export; docs:check green;
docs:build page count unchanged (179 pages, 125 nodes, 6 mounts).

* refactor(docs): split protocols out, rename internal to development

The docs/ tree is organized by audience, but docs/internal/ was a mixed
bag: it held the WebSocket protocol surface (published to the site as
Protocols > WebSocket) alongside contributor-only material, and an
integration guide that belonged with the other integrations.

- New docs/protocols/ holds both members of the site's Protocols section:
  websocket/ (was docs/internal/engine-protocol/) and mcp/ (was docs/mcp/).
  The section's two halves previously lived in unrelated trees.
- docs/internal/ -> docs/development/. Every file that remains is
  contributor documentation: setup, builder, engine internals, node
  authoring, pre-commit hooks. The old name also read as company-private
  in a public repository, which is the opposite of the signal contributor
  docs should send.
- docs/internal/n8n.md -> docs/product/integrations/n8n.md and onto the
  spine, joining the six existing integration guides. Prepared for
  publication: frontmatter added, repo-relative links rewritten to site
  routes or blob/develop URLs, and a reference to a local-only test
  harness removed.

Every folder under docs/ is now entirely published or entirely not.
docs/README.md documents that invariant and groups the sections by it.

Paths updated in gather.js (mounts + root glob), export.js (MCP readme
source), spine.js (integrations), AGENTS.md, nodes/AGENTS.md,
docs/README.md, .coderabbit.yaml, ci.yml, SUPPORT.md, examples/README.md,
tool_n8n's README, both validators, and the regenerated MCP package README.
Both validators also reformatted to the repo's ruff style.

Verification: docs:test 42/42, docs:check in sync, docs:build green,
docs:gather 180 pages (179 + the newly published n8n page) / 125 nodes /
6 mounts. Node and client validators unchanged in behavior.

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

* ci(docs): build the docs site on doc-touching PRs; run client parity checker

Review of the doc-CI surface found two genuine gaps; everything else was
judged adequate or deliberately rejected to avoid CI sprawl.

Gap 1: the docs site was only ever built AFTER merge (docs.yml on push to
develop), so deterministic breakage — broken internal links, docs/ files
not covered by a mount, spine entries without a page — merged clean and
failed on develop's deploy. New path-filtered docs-build job runs
docs:test + docs:build on PRs touching doc sources, wired into ci-ok
(skip-tolerant, same pattern as helm/shell-contract). Blocking is
justified: every failure mode is deterministic, no network flakiness.

Gap 2: scripts/validate-client-docs.py was committed but nothing executed
it. Added to the advisory node-docs job (renamed 'Doc schemas (advisory)').

Rejected as sprawl: an external-link checker (network-flaky by
construction), prose/spell linting (noise on a technical corpus), a
separate node-site-coverage check and a docs-folder-invariant check (both
subsumed — the first by the advisory schema validator, the second by
docs:build's unmounted-file abort now running on PRs).

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

---------

Co-authored-by: Krish Garg <krish.garg@rocketride.ai>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* docs(nodes): ship example bundles for seven nodes, then park the requirement (#1960)

* docs(agents): make AGENTS.md the single-source contributor router

AGENTS.md becomes the one canonical guide: repo overview, task routing
table into docs/, common commands (absorbed from .claude/CLAUDE.md),
conventions, and the documentation rules. .claude/CLAUDE.md becomes an
@AGENTS.md import and .cursorrules a pointer, eliminating the three
divergent copies of the doc-location rules. A nested nodes/AGENTS.md
gives agents working in the nodes tree the README schema and validator
command with zero indirection. docs/README.md references the
documentation contracts in docs/internal/.

* docs(nodes): add node README schema and deterministic validator

docs/internal/node-readme-schema.md defines the per-node README contract:
a fixed section set where services*.json triggers decide which sections
are required (invoke→Connections, lanes→Lanes, tool∈classType→As a tool,
≥2 profiles→Profiles, gpu→Requirements, restricted capabilities→
Limitations), core sections for every node (summary, What it does,
Configuration usage guidance, Example pipelines), and optional sections
with placement rules (About vendor, Authentication, Notes, Upstream
docs). The generated ROCKETRIDE:GENERATED:PARAMS region owns the field
reference; hand-written Configuration is usage guidance only.

scripts/validate-node-readme.py enforces the structural rules per node:
section presence/order, forbidden-when-untriggered, table parity against
declared invoke keys/lanes/profiles, and warnings when a field with
objective complexity signals lacks a detail subsection. Multi-service
directories are merged; fragment-only files (no protocol) are skipped.

* docs(nodes): migrate six sample node READMEs to the README schema

llm_anthropic, db_postgres, tool_github, store_qdrant, ocr, and
agent_deepagent restructured to the schema in
docs/internal/node-readme-schema.md. Content-preserving: existing prose
kept, sections reordered into the canonical layout, bespoke headings
folded into their owning sections or Notes, and the generated
ROCKETRIDE:GENERATED:PARAMS regions untouched byte-for-byte.

Substantive fixes made along the way:
- llm_anthropic: Profiles table now lists all 21 declared profiles
  (previously 7 — Opus 5, Fable 5, Sonnet 5, 4.8 variants, latest
  aliases and 3 Haiku were declared in services.json but undocumented)
- tool_github: removed a duplicated all-tools table that shadowed the
  per-group tool tables; grouped tables are now the single source
- All six gain Example pipelines; db_postgres gains the Limitations
  section its noremote capability requires; vendor nodes gain About
  sections

Validator: Profiles parity accepts key-or-title in the first two
columns; grouped detail subsections satisfy the complex-field check via
token containment. Schema: preset services with identical wiring may be
documented once (db_postgres/Supabase case).

* ci(docs): advisory node README schema check + CodeRabbit doc rules

New node-docs CI job runs scripts/validate-node-readme.py over the node
tree on every PR. Advisory for now (continue-on-error, not in ci-ok):
the corpus is mid-migration to the schema, so the job surfaces results
without blocking merges; flipping it to a hard gate is a two-line change
once migration completes.

CodeRabbit path_instructions direct review attention where the
deterministic validator cannot reach: prose accuracy of node READMEs
against code/services*.json (structure is machine-checked), and
Python/TypeScript client-doc parity.

* docs(clients): client readme schema, parity checker, and parity fixes

docs/internal/client-readme-schema.md codifies the shared section order
the Python and TypeScript readmes already follow, fixes the insertion
points for language-specific sections, and states the parity rule: any
documented method/type/exception exists in both readmes (snake_case and
camelCase compare equal) or carries a language-specific marker.

scripts/validate-client-docs.py enforces it deterministically: section
presence/order/ownership plus API symbol parity harvested from method
tables across the six shared API sections.

Parity gaps it found, fixed against client source:
- python readme was missing the attach/detach/is_attached/login/logout/
  is_authenticated/set_env connection lifecycle (implemented in
  mixins/connection.py, previously undocumented) and Answer.setAnswer
- typescript readme was missing Question.addGoal (Question.ts:264)
- dap_request (python convenience) and withConnection (typescript
  convenience) marked language-specific with cross-references

Package READMEs regenerated via docs:export; docs:check green;
docs:build page count unchanged (179 pages, 125 nodes, 6 mounts).

* refactor(docs): split protocols out, rename internal to development

The docs/ tree is organized by audience, but docs/internal/ was a mixed
bag: it held the WebSocket protocol surface (published to the site as
Protocols > WebSocket) alongside contributor-only material, and an
integration guide that belonged with the other integrations.

- New docs/protocols/ holds both members of the site's Protocols section:
  websocket/ (was docs/internal/engine-protocol/) and mcp/ (was docs/mcp/).
  The section's two halves previously lived in unrelated trees.
- docs/internal/ -> docs/development/. Every file that remains is
  contributor documentation: setup, builder, engine internals, node
  authoring, pre-commit hooks. The old name also read as company-private
  in a public repository, which is the opposite of the signal contributor
  docs should send.
- docs/internal/n8n.md -> docs/product/integrations/n8n.md and onto the
  spine, joining the six existing integration guides. Prepared for
  publication: frontmatter added, repo-relative links rewritten to site
  routes or blob/develop URLs, and a reference to a local-only test
  harness removed.

Every folder under docs/ is now entirely published or entirely not.
docs/README.md documents that invariant and groups the sections by it.

Paths updated in gather.js (mounts + root glob), export.js (MCP readme
source), spine.js (integrations), AGENTS.md, nodes/AGENTS.md,
docs/README.md, .coderabbit.yaml, ci.yml, SUPPORT.md, examples/README.md,
tool_n8n's README, both validators, and the regenerated MCP package README.
Both validators also reformatted to the repo's ruff style.

Verification: docs:test 42/42, docs:check in sync, docs:build green,
docs:gather 180 pages (179 + the newly published n8n page) / 125 nodes /
6 mounts. Node and client validators unchanged in behavior.

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

* ci(docs): build the docs site on doc-touching PRs; run client parity checker

Review of the doc-CI surface found two genuine gaps; everything else was
judged adequate or deliberately rejected to avoid CI sprawl.

Gap 1: the docs site was only ever built AFTER merge (docs.yml on push to
develop), so deterministic breakage — broken internal links, docs/ files
not covered by a mount, spine entries without a page — merged clean and
failed on develop's deploy. New path-filtered docs-build job runs
docs:test + docs:build on PRs touching doc sources, wired into ci-ok
(skip-tolerant, same pattern as helm/shell-contract). Blocking is
justified: every failure mode is deterministic, no network flakiness.

Gap 2: scripts/validate-client-docs.py was committed but nothing executed
it. Added to the advisory node-docs job (renamed 'Doc schemas (advisory)').

Rejected as sprawl: an external-link checker (network-flaky by
construction), prose/spell linting (noise on a technical corpus), a
separate node-site-coverage check and a docs-folder-invariant check (both
subsumed — the first by the advisory schema validator, the second by
docs:build's unmounted-file abort now running on PRs).

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

* feat(docs): shipped example bundle — screenshot + downloadable .pipe per node

Every node's Example pipelines section now opens with a shipped example:
a minimal runnable pipeline committed as example.pipe next to the node,
a canvas screenshot of that same pipeline (example.png), and a shields.io
download badge. Readers browsing the docs see the node wired on the
canvas and can download the .pipe straight into their own canvas.

- Schema: the standalone optional screenshot (formerly under What it
  does) is folded into Example pipelines as part of the bundle. Authors
  reference example.png / example.pipe by bare relative name so node
  folders stay self-contained and GitHub renders them natively.
- gather.js: stageNodeMarkdown rewrites those two refs to develop-pinned
  repository URLs when staging site pages (raw for the image, blob for
  the .pipe download) — same pattern as the generated Source link.
  Verified end-to-end on a staged page.
- Validator: bundle is all-or-nothing (pipe file, screenshot file, embed,
  download link + alt text) — any part present makes every part required;
  nothing at all is a warning until the node's bundle lands. New nodes
  must ship it (CodeRabbit instruction added).

docs:test 42/42, staging parity (180 pages / 125 nodes / 6 mounts),
6 samples still pass (now with the expected bundle warning), ruff clean.

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

* docs(nodes): move Example pipelines above the reference sections; migrate agent_llamaindex

Example pipelines now sits directly under What it does, so a reader sees
what the node is, a canvas screenshot, and a downloadable .pipe before any
reference tables. Schema sections renumbered; the six migrated samples
reordered to match.

agent_llamaindex migrated to the schema: About LlamaIndex, three example
pipelines, Connections/Lanes/As a tool tables matching services.json, and
Configuration reduced to usage guidance — the duplicated hand-written
field table is gone (the generated Schema table owns it), with detail
subsections for the three fields that need them (Agent description as the
parent-agent selection signal, Instructions, Require tool call as the
fabrication guard). Loop internals and SSE progress moved under Notes.

7/125 nodes pass. docs:test 42/42, staging parity, ruff clean.

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

* feat(docs): trigger the example bundle on references, ship agent_llamaindex's .pipe

The all-or-nothing rule counted a loose file as a bundle part, so
committing example.pipe before its screenshot existed would fail the node
— blocking the natural two-step path (author the pipeline, screenshot it
later). The trigger is now a README *reference*: referencing either half
obliges the other and every referenced file must exist (no broken links,
never a half-rendered README), while an unreferenced file is a warning
naming what is present and what is still needed.

agent_llamaindex's example.pipe is the existing, working
examples/agent-llamaindex.pipe — chat -> agent (llm_anthropic on the llm
channel, tool_http_request on tool) -> response_answers — rather than an
authored approximation. Its screenshot and the two references land when
the pipeline is captured on the canvas.

Verified all three paths: unreferenced file warns, a lone badge fails,
a referenced-but-missing file fails. docs:test 42/42, staging parity,
7/125 nodes pass, ruff clean.

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

* docs(nodes): ship example.pipe for all seven migrated nodes; fix a broken example

Every migrated node now carries the .pipe half of its example bundle, so
completing each one is capture-a-screenshot plus two pasted lines.
docs/development/example-bundle-todo.md holds the per-node instructions,
the exact markdown to paste (alt text written per node), and provenance.

Three are existing working pipelines copied from examples/:
agent_llamaindex <- agent-llamaindex.pipe, ocr <- document-processor.pipe,
store_qdrant <- rag-pipeline.pipe. The other four (llm_anthropic,
db_postgres, tool_github, agent_deepagent) were authored from those proven
shapes and statically validated: every provider is a declared node
protocol, every input lane is one the upstream node produces, every control
channel is declared in the target's invoke. They are unrun — opening each
on the canvas for its screenshot is the verification step.

Also fixes examples/document-processor.pipe, which referenced provider
'anonymize' while the node declares 'anonymize_text' — a broken example
that predates this branch. All 17 files in examples/ now reference only
real providers.

7/125 nodes pass, each with the expected unreferenced-bundle warning.
docs:test 42/42, staging parity.

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

* feat(docs): centre the example screenshot and download button, matching the root README

The markdown image form rendered left-aligned at natural size; the root
README gets its look from an HTML block, so node examples now use the same
house style: a centred div with a width=100% screenshot above the
shields.io download button. Safe on the docs site because Docusaurus is
configured markdown.format 'detect', so .md is parsed as CommonMark rather
than MDX (tags are still self-closed for portability).

- gather.js: the staging rewrite now matches HTML attribute forms (src=,
  href=) as well as markdown targets, so the centred block resolves to
  repository URLs on the site. The shields.io URL is deliberately untouched.
- Validator accepts either form and reads alt text out of the img tag, so
  the alt-text requirement still holds.
- Schema documents the HTML block as house style and records why the link
  stays relative: the VS Code extension registers a custom editor for
  *.pipe, so clicking the button in the IDE opens the pipeline directly on
  the canvas — an absolute URL would bounce the reader to a browser.
- agent_llamaindex converted, its example.png committed, and its first
  example corrected to describe the pipeline actually shipped (chat +
  tool_http_request, not webhook + tool_tavily). First fully complete
  bundle in the repo.
- Checklist snippets regenerated in the new form.

docs:build green (HTML compiles on the site), docs:test 42/42, 7/125 nodes
pass, ruff clean.

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

* fix(docs): keep the example block renderable in the VS Code preview

The raw-HTML form centred correctly on GitHub but rendered as empty space
in the VS Code / Cursor markdown preview: that preview rewrites relative
paths only for markdown-syntax images, so an <img src="example.png"> is
left unresolved and blocked. The IDE is the surface where the download
button matters most (clicking it opens the pipeline on the canvas), so it
cannot be the one that breaks.

Blank lines inside the wrapper fix it. A blank line ends the HTML block per
CommonMark, so the image and badge parse as markdown — resolving in the
preview — while the surrounding div still centres them, and GitHub scales
the screenshot to the content width on its own. Renders in all three
surfaces: IDE preview, GitHub, and the docs site.

Schema records why the blank lines are load-bearing so nobody tidies them
away. Checklist snippets regenerated.

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

* docs(nodes): complete shipped example bundles

* docs(nodes): park the Example pipelines section and its bundle checks

The shipped-example bundle (canvas screenshot + downloadable .pipe) is
complete for seven nodes but is a per-node manual step, and it blocks the
migration of the remaining ~120 READMEs behind screenshot capture. Park
the requirement so the migration can proceed on prose alone.

`## Example pipelines` is commented out in the schema with its full text
preserved in place, and the validator's bundle checks are commented out
alongside it — un-parking is uncommenting both and re-numbering the
schema's sections.

The section stays in the validator's CANONICAL_ORDER, so it remains
tolerated in its slot rather than flagged as an unknown heading: the
seven migrated nodes keep their examples and keep passing. It is simply
no longer required. gather.js keeps rewriting relative example refs for
the staged site — inert with nothing to rewrite, needed when this returns.

* ci: scope the doc schema checks to fix/docs, leave the shared pipeline alone

The doc schema work added four things to config that every branch shares:
a docs path filter and two new jobs in ci.yml, a pnpm install plus a
docs:check step bolted onto the always-on ruff-check job, and two path
instructions in .coderabbit.yaml. None of that belongs on develop while
the schemas are still being rolled out — and the ruff-check change in
particular would have added an install and a docs build to every pull
request in the repository, not just documentation ones.

ci.yml is restored byte-for-byte to its pre-branch state, so merging
fix/docs into develop is now a no-op for the shared pipeline.

The same checks now live in .github/workflows/docs-schemas.yml, which
triggers only on pull requests and pushes targeting fix/docs. It stays
inert on develop even after this branch merges. Validators are advisory
(the node corpus is mid-migration and would paint every PR red); the
site build and export drift gate are blocking, since both pass today.

CodeRabbit has no per-branch config file, but it reads .coderabbit.yaml
from a pull request's base branch — so adding fix/docs to base_branches
here enables review for PRs into this branch while develop keeps using
its own copy. That entry and the two doc path instructions are fenced
with TEMPORARY markers to be removed in the fix/docs -> develop PR.

* ci: drop the unusable workflow_dispatch trigger from the doc schema checks

workflow_dispatch only fires when the workflow file lives on the
repository's default branch. This file is deliberately scoped to
fix/docs and is meant never to reach develop, so the trigger could
never have worked — gh confirms it with 'workflow not found on the
default branch'. Removed rather than left as dead configuration.

* chore: drop agent stubs the VS Code extension planted in this repo

The extension's agent installer (apps/vscode/src/agents/) writes AI
assistant instruction files into whatever workspace is open, on startup,
with rocketride.integrations.autoAgentIntegration defaulting to true. It
adds its own .rocketride/ docs directory to .gitignore but not the stubs
it plants, so three of them were swept into b7c9918f, and its
marker-based merge appended a thirtieth-line block to AGENTS.md because
that file had no ROCKETRIDE markers to replace.

That block is the reason CodeRabbit flagged AGENTS.md as
self-contradicting on #1960: line 21 sends contributors to
docs/agents/ROCKETRIDE_QUICKSTART.md while the appended block made
.rocketride/docs/ROCKETRIDE_README.md step 1. AGENTS.md is the
contributor router, and .rocketride/docs/ is customer-facing SDK
documentation — the wrong guidance for someone working on the engine.

Removes the three files and the appended block, and gitignores the stub
targets not already covered (.cursor/ and .claude/* already were, which
is the only reason the Cursor stub never landed). AGENTS.md cannot be
gitignored, so the installer is also disabled in .vscode/settings.json,
which is tracked and therefore fixes this for everyone who opens the
repo rather than one machine.

The underlying extension bug is not fixed here.

---------

Co-authored-by: Krish Garg <krish.garg@rocketride.ai>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* ci(docs): scope the node schema check to the PR's diff, and make it blocking (#1971)

* ci(docs): gate the node schema on the PR's own diff, and make it blocking

The corpus-wide run was advisory because 109 of 124 nodes fail the schema
today, so a hard gate would have been red on every PR from the day it was
added. But a check that has always failed is a check nobody reads, and the
day it fails for a new reason looks identical to the two hundred days
before it. The interesting failures were going to be buried under
structural noise like "unknown heading '## Modes'".

Scoping it to the nodes the PR actually touches inverts that: green by
default, so red means this PR broke something. It also lets the check be
a hard gate immediately rather than waiting on a 109-node cleanup to
finish before it protects anything, and the backlog drains on its own —
touching a node obliges you to bring its README along — while never being
able to grow.

services*.json counts as a trigger alongside README.md, since the metadata
is what decides which sections are required or forbidden; a metadata-only
change can invalidate a README nobody edited. That also closes the gap
where the docs path filter watched only README.md.

--all keeps a home in a new informational job that writes pass/fail counts
to the run summary, where a large number is a trend line rather than a
merge blocker. Retire that job when the count reaches zero.

Checked against the existing docs:check gate to be sure two checks cannot
disagree about what a good README is: docs:check verifies that exported
client package READMEs match their docs/ sources, which is export drift,
not document structure. No overlap.

Verified under bash (CI's shell, not the zsh this was written in, which
does not word-split unquoted variables): 8 nodes detected from the diff,
all pass, exit 0; including an unmigrated node exits 1.

* ci(docs): stop the scoped schema gate failing on a diff with no node files

Two bugs CodeRabbit caught in the previous commit, both reproduced before
fixing.

Under `set -euo pipefail`, grep exits 1 when nothing matches, so a PR that
touched no node README or services*.json died on the command substitution
before reaching the empty check that was meant to report a clean skip.
Since that job is now blocking, an unrelated PR into fix/docs would have
been blocked outright. The `-d` test in the trailing loop had the same
shape when the last candidate was a deleted node.

`grep -c` already prints 0 when it matches nothing and exits 1 as well, so
`|| echo 0` emitted a second zero and the run summary rendered "0 0".

Verified: empty diff reaches the skip path and exits 0; the real diff still
resolves 8 nodes; counts render as a single 0.

---------

Co-authored-by: Krish Garg <krish.garg@rocketride.ai>

* fix(docs): stop the node validator eating underscores in identifiers (#1973)

`table_col` stripped `[*_`]` from every table cell to remove markdown
decoration before comparing names against services*.json. Underscores are
markdown emphasis, but they are also legal in the identifiers this
function exists to compare, so the strip silently corrupted them:
`_source` became `source` and no longer matched the lane that declared it.

Caught on tool_pipe, whose README was correct and failed anyway. Thirteen
nodes carry an underscore in a lane, connection, or profile name, and the
LLM family is the worst affected — `gemini-2_5-flash`, `llama3_2-vision-11b`,
`ai21_jamba-1_5-large` — so this would have produced a wave of false
failures on the largest batch still to come.

Profiles masked the bug because their comparison falls back to fuzzy
containment across two columns; lanes and connections compare strictly,
so they failed outright.

Emphasis is now only removed when a pair of underscores wraps the whole
cell, which is the only case where an underscore is decoration rather than
part of a name. Verified against `_source`, `z_score`, `gemini-2_5-flash`,
`streamable_http`, `_emphasised_`, and both the `*(default)*` and
`_(default)_` markers already in use. No change to the corpus pass count.

Co-authored-by: Krish Garg <krish.garg@rocketride.ai>

* refactor(docs): reorganize docs/ by audience and retire duplicated pages (#1969)

* refactor(docs): reorganize docs/ by audience and retire duplicated pages

Collapses docs/ to three top-level folders keyed by who reads them, brings all
five clients under one roof, and removes the hand-written pages that duplicated
generated node documentation.

docs/
  public/       humans outside the repo: typescript, python, vscode, mcp, n8n
                (each with a distribution README.md and assets/), product/ (the
                site spine), assets/
  development/  contributors; never published, no exceptions
  agents/       AI assistants: ROCKETRIDE_*.md, stubs/, skills/

clients/, protocols/, stubs/ and images/ are dissolved. Site mount destinations
are unchanged, so no published URL moves except where noted below.

Five clients, one place. mcp graduates from protocols/ to a client folder (its
README already was the published rocketride-mcp README). n8n joins as the fifth:
packages/n8n-nodes/README.md is now generated by docs:export like the other
three, so it can no longer drift.

Integration pages retired. Six of seven pages under product/integrations were
node-README duplicates at 73-90% overlap, and postgres.md had already drifted
into publishing Supabase instructions that the db_postgres README says will fail
to resolve. All six are deleted and redirected to their node pages; n8n survives
because it documents webhook ingress, credential-vault and human-in-the-loop
flows that no single node README can own.

development/ is now genuinely internal. Its websocket docs split by audience:
the wire-protocol reference (external integrators) moved to the site spine and
keeps its URLs, while crash-reporting.md (lldb, breakpad) is unpublished. apps.md
split likewise, its published-SDK half becoming develop/apps with shell-ui
imports normalized to rocketride/app-sdk. gather no longer sweeps development/
at all, so it cannot publish by accident. The remainder is grouped by subsystem
(builder/, engine/, nodes/, clients/, apps/).

product/ regrouped. The real quickstart was filed under Evaluate while
/quickstart was an empty card grid pointing at it; it is now a landing page with
the tutorial split into an IDE walkthrough and an SDK guide. Loose root files
became operate/ (cloud, self-hosting, troubleshooting) and reference/ (cli,
glossary, pipeline-reference).

Broken distribution assets fixed. Every banner in the published READMEs pointed
at a main-pinned raw URL for a path that had never existed there, so all of them
404 on npm, PyPI and the Marketplace. They now target the per-client assets/
paths; the root README uses relative paths that resolve on any branch.

New guardrail: a doc id is a public URL here, and a spine id with no backing file
used to publish a silent "coming soon" page. assertNoUnexpectedPlaceholders now
fails the build and names the offending id, with an allowlist for intentional
stubs. It caught four moved URLs hardcoded in the site footer during this work.

Also adds development/docs-pipeline.md and development/ci-gates.md, deletes a
service catalog that had drifted to 118/148 against a real 143/163, and fixes
setup-guide and root-file link depths.

Verified: docs:build 176 pages / 125 nodes / 5 mounts, docs:check in sync,
docs:test 48/48, validate-client-docs.py passing, docs:export idempotent. 14
retired routes all have emitted redirects.

* docs: address CodeRabbit review on #1969

Twelve of thirteen findings accepted; several were symptoms of wider problems
and were fixed at the root.

Wrong technical claims, verified against the source and rewritten:
- Data lanes and agent tools were described as mutually exclusive. Nine of
  27 `tool` services declare lanes, and validate-node-readme.py gates the two
  sections independently. Replaced with a 2x2 matrix.
- The node loader contract was pseudocode for a `MyNode`/`process()` API that
  does not exist. python-instance.cpp requires `IInstance`, python-global.cpp
  treats `IGlobal` as optional, and bindMethod binds callbacks by name from a
  24-entry list, silently no-oping on a typo. Replaced with a working
  three-file template.
- `./builder test server` is not an alias for the server tests: bare args fan
  out to every module. Corrected here and two more sites in index.md.

Host and path corrections:
- The SDK `uri` examples used cloud.rocketride.ai, which is the web console.
  The API host is api.rocketride.ai (CONST_DEFAULT_WEB_CLOUD in both SDKs
  since #1078). Only SDK/URI contexts were changed; ~20 sites across the
  Python, TypeScript and VS Code pages, which had drifted away from the
  package READMEs that were already correct.
- The exported agent doc linked into docs/development/, which docs:export
  never copies to .rocketride/docs/. Repointed at a sibling exported doc and
  published URLs.
- docs.rocketride.ai -> .org in apps/index.md, matching docusaurus.config.ts.

Also: documented the four app-SDK methods missing from the API list, corrected
the n8n trigger's authentication description (it declares no credentials, only
a `secret` parameter checked against the Authorization header), fixed rsbuild
dependency drift in the app template, normalized bare `builder` to `./builder`
in 14 places, tagged three untagged code fences, and ran prettier over
redirects.ts (all 702 entry lines predated this PR; array data verified
identical before and after).

Rejected: the suggestion to unify updateEditorViewState and
updateEditorViewport. They are distinct methods with different signatures in
both Documents.tsx and shell-api/v0.d.ts; unifying them would introduce a bug.
Also rejected `npx` -> `pnpm exec`, which the apps' own tasks.js contradict.

Two source-level issues found and left for separate PRs: app-sdk/index.ts is
missing four methods the runtime exposes, and tool_firecrawl/README.md was
already schema-nonconformant.

* docs(nodes): migrate the tool_ family to the README schema (#1974)

* docs(nodes): migrate tool_apify, tool_bland_ai, tool_chartjs, tool_cognee, tool_daytona, tool_deepl to the README schema

* docs(nodes): migrate tool_exa_search, tool_filesystem, tool_firecrawl, tool_git, tool_guild, tool_http_request to the README schema

* docs(nodes): migrate tool_laserdata_memory, tool_mcp_client, tool_mem0, tool_n8n, tool_oura, tool_pipe to the README schema

* docs(nodes): migrate tool_google_workspace, tool_pipedrive, tool_python, tool_slack, tool_tavily, tool_v0, tool_xtrace_memory to the README schema

* docs(tool_pipedrive): clarify bearer token format

* docs(nodes): use American spelling in tool_n8n

---------

Co-authored-by: Krish Garg <krish.garg@rocketride.ai>

* docs(nodes): migrate the LLM family with progressive model tables (#2022)

* docs(nodes): migrate LLM family with progressive model tables

* docs(nodes): settle the Profiles contract before the other families rebase

This PR's schema and validator changes become the rules the five in-flight
node-README PRs are held to, so four gaps get closed here rather than after
74 more nodes have adopted them.

Each protocol-bearing service keeps its own default. A second registration is
a separate node to the engine — a branded preset, a second provider, a second
backend — so its preconfig.default is a fact about that service, not a
competing claim about the primary one. The old single-default rule false-failed
cloud_tts (#1975) and store_elasticsearch (#1970), which are correct as
written, and it also hid a real defect here: llm_openai_api's Nebius default
llama-3-3-70b rendered unmarked, and the intro bolded a title the metadata
does not declare. Both fixed. Four tests cover the multi-service case; there
were none.

The visible-table rule said "until the table reaches six rows" while the
validator enforced "at most six", so ten of twelve large-layout nodes
under-fill against the written text. Six is now stated as a ceiling, not a
quota, with an explicit clause for the nodes that front several vendors'
catalogues — llm_bedrock was collapsing Llama 4 Scout and Maverick while
showing Llama 3.3 above the fold, which that clause now forbids and this
change corrects.

How a row names its profile is now stated: the declared key as code, or the
declared title reproduced exactly, one form per node. That is what the
validator already enforced through resolve_profile_row, but leaving it
unwritten is why guardrails (#1978) fails on re-cased display names.

The default marker is **(default)**. The validator stays decoration-agnostic
so pages using *(default)* keep passing; the schema now says which to write.

No regression across the 124-node corpus: same failure set before and after.
21 validator tests pass.

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

---------

Co-authored-by: Dylan Savage <dylanmsavage@hotmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* docs(nodes): migrate the store_ family to the README schema (#1970)

* docs(nodes): migrate store family to README schema

* docs(nodes): name each store profile default in its Profiles intro

#2022 landed the tightened Profiles contract: every protocol-bearing service's
declared default has to appear before the first table, with its key and its
title in bold, and be marked on its row. Six of the store nodes marked the row
but never named the default in prose, so they fail the new validator.

store_elasticsearch registers two backends with two separate defaults —
Elasticsearch defaults to self-managed, OpenSearch to local. Both rows were
already marked, which read as a contradiction with nothing explaining it; the
intro now says which default belongs to which service. That shape is what the
new multi-service clause in the schema describes.

store_qdrant is not part of this migration — it was already on fix/docs and
already passing the old validator — but it is the same one-line fix in the same
family and it fails the new rules, so it is folded in here rather than left as
an orphan nothing re-checks.

Default markers switched from *(default)* to **(default)**, which is the form
the schema now specifies. The validator accepts either.

All nine store nodes pass; 21 validator tests pass.

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

---------

Co-authored-by: Dylan Savage <dylanmsavage@hotmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* docs(nodes): migrate the small node families to the README schema (#1975)

* docs(cloud_tts): seed the generated params region so the node reaches the docs site

gather.js stages a node only when its README carries the
ROCKETRIDE:GENERATED:PARAMS marker, and nodes:docs-generate refuses to
create one (resolveDocPath returns null without it), so a node that never
had the marker can never acquire it on its own. cloud_tts was in that
state and has been invisible on the site.

Seeded an empty marker pair and ran the generator to fill it, which
produced both service tables (ElevenLabs and OpenAI). Content is entirely
machine-generated; nothing here is hand-written.

* docs(nodes): migrate agent_crewai, agent_langchain, agent_rocketride, cloud_tts, db_clickhouse, db_mysql to the README schema

* docs(nodes): migrate embedding_image, embedding_openai, embedding_transformer, embedding_video, graph_arango, graph_falkordb to the README schema

* docs(nodes): migrate graph_hydradb, graph_neo4j, memory_internal, memory_persistent, preprocessor_code, preprocessor_langchain, preprocessor_llm to the README schema

* fix(docs): clarify supported OpenAI embedding profiles

* fix(docs): document embedding API key resolution

* fix(docs): list custom node profiles

* docs(nodes): bring the small families up to the new Profiles contract

Nine nodes fail the contract #2022 landed. Most need only the intro sentence
naming the declared default, but three carried real gaps:

embedding_transformer and memory_persistent never listed their `custom`
profile, so a profile the configuration panel offers was undocumented.
memory_persistent went further and explained the omission in prose; the row is
now present and that sentence says what `custom` is for instead.

embedding_openai rendered its declared context window as "8191 tokens." in a
semantic column, which the validator compares against the metadata value. The
column is now `Context tokens` carrying the number alone.

cloud_tts registers OpenAI and ElevenLabs with a default each. Both rows were
marked with nothing explaining why there were two; the intro now names each
default and the service it belongs to. Its Profile column also mixed bare keys
for one provider with key-plus-title for the other — every row now uses the
declared title, one convention per node as the schema asks.

Profile titles are padded to align in the configuration panel's dropdown
('Text Small   - ...', 'Text Large   - ...'). That alignment is deliberate and
belongs in services.json, but requiring a README to reproduce it verbatim in
bold prose is not reasonable, so title matching now collapses whitespace runs
on both sides. Two tests cover it, including that a genuine wording mismatch
still fails.

All 19 nodes in this PR pass; 23 validator tests pass.

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

* docs(preprocessor_code): correct the parser cache's scope

The README said the parser cache is retained for the lifetime of the global
node instance. languageParsers is declared as a class attribute on
PreProcessor (code.py:166) and the write at code.py:235 is an item assignment,
so it mutates the class-level dict rather than rebinding a per-instance one. A
grammar loaded once is therefore reused by every instance in the process and
outlives any single global node instance.

Raised by CodeRabbit on #1975.

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

---------

Co-authored-by: Krish Garg <krish.garg@rocketride.ai>
Co-authored-by: Dylan Savage <dylanmsavage@hotmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* docs(nodes): migrate the media nodes, and document eight that never had a README (#1976)

* docs(nodes): migrate detect_segment, face_detection, pose_estimation, video_composer to the README schema

* docs(nodes): migrate background_removal, caption, depth_estimate, detect to the README schema

* docs(nodes): migrate accessibility_describe, frame_grabber, image_cleanup, thumbnail to the README schema

* docs(nodes): migrate audio_player, audio_transcribe, audio_tts, twelvelabs to the README schema

* docs(nodes): seed generated params regions for the eight newly documented nodes

These nodes had no README at all, so they had no marker region either, and
nodes:docs-generate cannot create one (resolveDocPath returns null when the
marker is absent). Seeded an empty pair per node and ran the generator to
fill it. Content is entirely machine-generated.

* fix(docs): clarify audio player video input

* fix(docs): clarify background removal output size

* fix(docs): label RT-DETR fallback link

* docs(nodes): bring the media nodes up to the new Profiles contract

Eight nodes needed the intro sentence naming the declared default, which is
all the new contract asks of this family — no missing rows, no semantic-column
mismatches, no multi-service defaults. Markers switched to **(default)**.

The review also turned up a boilerplate problem worth fixing while the family
is open. Four ## Requirements sections opened with "This node is marked as
GPU-capable. Its metadata declares local CPU, Apple Silicon (MPS), and CUDA
operation" and closed with a variant of "The source does not specify VRAM
requirements or relative CPU performance." Both halves describe the metadata
and the writing process rather than the node: a reader who opens ## Requirements
wants to know what hardware to provision, and being told what the source failed
to mention answers nothing. Those sections now say where inference runs and what
the device lock does, and stop. depth_estimate keeps the one fact the closer was
carrying — its output is relative depth, not calibrated distance.

Same fix in two other places: caption pointed at sibling nodes via "the node
metadata directs object-detection use cases to Object Detection", and
audio_player attributed its own truncation note to "The source notes that...".

All 16 nodes pass.

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

---------

Co-authored-by: Krish Garg <krish.garg@rocketride.ai>
Co-authored-by: Dylan Savage <dylanmsavage@hotmail.com>

* docs(nodes): migrate the text, NLP, and document-parsing nodes (#1977)

* docs(nodes): migrate dictionary, extract_data, extract_facts, ner to the README schema

* docs(nodes): migrate anomaly_detector, anonymize, answer_documents, currency_convert_explicit to the README schema

* docs(nodes): migrate prompt, question, schema_validate, summarization to the README schema

* docs(nodes): migrate landing_ai, llamaparse, reducto, vectorizer to the README schema

* docs(landing_ai): seed the generated params region so the node reaches the docs site

Last of the thirteen nodes gather.js was silently skipping. Its two variant
sub-pages (extract, parse) were hidden with it, since variants are only
staged once their parent is.

* fix(docs): clarify anonymizer classifier fallback

* fix(docs): use canonical LlamaParse links

* fix(docs): document JSON scalar lane metadata

* fix(docs): use American spelling consistently

* fix(docs): clarify severity fallback warnings

* docs(nodes): bring the text family up to the new Profiles contract

Three nodes fail the contract #2022 landed. anomaly_detector needs only the
intro sentence naming the declared default; anonymize and ner also never listed
their `custom` profile, so a model choice the configuration panel offers was
undocumented in both.

The review turned up a second, larger thing. Seven nodes carried an
`## About RocketRide` section. That slot is for third-party services a node
wraps, and RocketRide is not third-party: on a page inside RocketRide's own
documentation, "RocketRide connects data-processing components into pipelines"
tells a reader nothing, and the sentence after it describes what the node does,
which is what `## What it does` is for and where each of these already says it.
Each one also dragged along an `## Upstream docs` link back to
docs.rocketride.org — circular, and present only because `## About` requires
that section. Both are removed from all seven.

question's `## Requirements` said the node "is marked GPU-capable in its
metadata, but its implementation only strips text". A reader opening that
section wants to know what to provision, not what the metadata claims, so it
now says the node needs no hardware of its own and that any GPU requirement
belongs downstream.

All 16 nodes pass.

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

---------

Co-authored-by: Krish Garg <krish.garg@rocketride.ai>
Co-authored-by: Dylan Savage <dylanmsavage@hotmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* docs(nodes): migrate the infrastructure and I/O nodes (#1978)

* docs(nodes): migrate local_text_output, telegram, text_output, webhook to the README schema

* docs(nodes): migrate autopipe, core, remote, response to the README schema

* docs(nodes): migrate aparavi_aql, rocketride_graph, rocketride_sql, rocketride_vector to the README schema

* docs(nodes): migrate guardrails, rerank_cohere, search_exa to the README schema

* fix(docs): explain local-only node execution

* docs(guardrails): document custom profile

* docs(search-exa): warn against production mock bypass

* docs(webhook): clarify endpoint security constraints

* docs(nodes): bring the infrastructure nodes up to the new Profiles contract

guardrails named its profiles "Basic", "Strict" and "Custom" — display names
the metadata never declares, so no row resolved to a profile and the declared
default was not marked on any of them. Rows now use the declared keys, and the
prose that referred to the display names follows. remote and rerank_cohere
needed the intro sentence; rerank_cohere also described its `custom` profile in
a trailing sentence rather than listing it, so a profile the configuration
panel offers had no row.

Five nodes carried an `## About` section that was not about a third-party
service. webhook and local_text_output were filler — "RocketRide is the
pipeline system that hosts this node", "Python provides the filesystem
functions used by this node" — and each dragged along an `## Upstream docs`
link back to docs.rocketride.org that existed only because `## About` requires
that section.

The three managed-database nodes were different: their About carried a real
fact, that RocketRide provisions a per-tenant database and the node resolves it
from the signed-in identity rather than from connection fields. That is
configuration guidance, and each `## Configuration` already gestured at it, so
it moves there in full rather than being dropped.

search_exa said the same thing twice: the third paragraph of `## What it does`
restated `## Notes` → URL safety almost sentence for sentence, and took the
section to nine sentences. The summary now states the guarantee and points at
Notes, which keeps the detail and regains the address classes the summary had
been carrying.

All 15 nodes pass.

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

---------

Co-authored-by: Krish Garg <krish.garg@rocketride.ai>
Co-authored-by: Dylan Savage <dylanmsavage@hotmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* docs(nodes): clear the last three nodes the migration PRs did not cover (#2124)

These three were already on fix/docs before the six family migrations, so no
PR owned them and nothing re-validated them once #2022 tightened the contract.

ocr was not a documentation problem. Its table is the best-formed profile
table in the corpus — twelve profiles with the key and the title in separate
columns — but the first column is headed "Profile key", which PROFILE_HEADERS
did not alias, so every row resolved to an empty cell and the validator
reported all twelve profiles missing and twelve unknown rows. The alias is
added, with a test, because separating key from title is a shape worth
supporting rather than a mistake to correct. ocr additionally needed its
default marked and its title in the intro.

tool_mcp_client registers MCP Client and the Butterbase preset, each with its
own default, and only the RocketRide one was marked — so the Butterbase
preset's default was undocumented. Both are now marked and named.

tool_laserdata_memory needed the intro sentence.

All 124 nodes pass; 24 validator tests pass.

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

* docs(mcp): split MCP docs into stdio and http, retire the module doc

The MCP docs described only the stdio PyPI package, while the HTTP MCP
server merged from develop (PR #1880) carried its docs as a 613-line
co-located doc.md — the wrong depth for a public surface, in a location
the docs tree does not allow.

- docs/public/mcp/ now holds two folders: stdio/ and http/, each with a
  README.md, rendered in the sidebar as an MCP category with two pages.
  The two near-duplicate stdio docs (README.md export source + index.md
  site page) are merged into stdio/README.md, which serves as both the
  /protocols/mcp/stdio page and the packages/client-mcp README source
  (regenerated copy included).
- docs/public/mcp/http/README.md is a new public doc sized to how
  GitHub, Sentry, and Linear document their hosted MCP servers:
  endpoint, auth in two sentences, per-client setup, the 27 tools by
  capability group, example prompts, resources, widgets, self-hosting
  env vars, security, FAQ.
- packages/ai/src/ai/modules/mcp/doc.md is deleted; the few contributor
  facts that live nowhere else (engine-client seam, credentials-catalog
  builder gates, widget workspace) move to
  docs/development/engine/mcp-module.md. Everything else stays in git
  history.
- Pipeline: DOCS_ROOT_MOUNTS split per folder; a README.md at a mount
  root with no index sibling is now gathered as that mount's page (the
  blanket export-source skip is otherwise unchanged); export mapping,
  spine, redirects, footer link, and spine tests updated; stale
  /protocols/mcp links retargeted to /protocols/mcp/stdio.

Verified: docs:build (broken-link gate), docs:check, spine tests,
validate-client-docs.py all green.

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

* docs: fix accuracy drift across the docs tree from a full code-verified audit

Every fix traces to a finding confirmed against source by an independent
adversarial verification pass (10 audit lanes, 10 verifiers, 10 fix
executors; ~252 confirmed fixes applied, 12 refuted findings dropped).

Highlights:
- SDK docs (TS/Python): remove fictional APIs and behavior (.env
  auto-parsing, exponential backoff, Answer.parseJson, dapRequest, MIME
  table), correct signatures/defaults, add missing parameter rows
- Examples: pipeline samples now validate (components key, real
  providers/fields, embedded question lanes for qdrant)
- WebSocket protocol pages: correct wire commands, token semantics,
  response shapes, event names; document the run-log continuum
- VS Code docs: migrate to the grouped settings schema, real command
  surface and workflows
- Development docs: builder module table from live --list output,
  ci-gates matches actual CI, runnable command fixes
- Operate/reference: ROCKETRIDE_APIKEY vs ROCKETRIDE_AUTH scoping, CLI
  table corrections, working tar command
- Regenerated client-mcp/client-python/client-typescript READMEs via
  docs:export

Gates: docs:build (broken-link check), docs:check, validate-client-docs.

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

* docs(agents): land the pipeline-builder skill set in docs/agents/skills/ (#2125)

* docs(agents): land the pipeline-builder skill set in docs/agents/skills/

First occupants of the reserved skills directory: the five hand-written
pipeline-builder skills (orchestrator + design/configure/run/debug) plus
MCP_TOOL_CONTRACT.md, the frozen tool-name/result-shape contract against the
27-tool HTTP MCP server surface.

The skills are weak-model-first: verification by tools (validate_pipeline is
the compiler, the run result is the proof) and hard gates (Waiting = STOP,
cost approval, 17 forcing functions in GATE_PROTOCOL.md) rather than model
intelligence. Bundled L1 node index regenerated 2026-08-25 from a live
v3.3.1 engine (124 nodes, matches this branch's README corpus).

Deliberately not exported by docs:export (subdirectories are excluded);
README documents the layout and the future export hook. Bundled shims
conformed to repo ruff style; anti-example keys shortened to obvious
placeholders that clear the gitleaks rules.

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

* docs(agents): apply review fixes to the pipeline-builder skill set

Code-verified review fixes on top of the initial drop:

- MCP_TOOL_CONTRACT: error envelope is {error_type, message, hint}; add
  pipelineTraceLevel to run_pipeline/run_dropper_pipe inputs (server
  default summary); list_integrations detail key is 'setup'
- L1 node index reconciled against the branch's node corpus: 167
  providers (renamed graph nodes, store tool flags, missing lanes, 45
  absent providers added); meta provenance + counts synced
- Error strings now match what the code emits verbatim (or are worded
  as symptom patterns): 'The service <provider> was not found',
  'references unknown component id', 'input has unknown lane',
  'Connection closed unexpectedly', 'Pipeline is already running.'
- ${ROCKETRIDE_*} prefix requirement restored in PIPELINE_ANTIPATTERNS
  and the rules summary (non-prefixed refs come back <REDACTED>)
- Shims: MIT headers, corrupt-cache reads follow the ERROR_JSON
  contract, fetch-doc docstring drift fixed, cross-skill hint path
- New tools/generate-index.py — the index regenerator three docs
  reference (SDK get_services(), atomic writes, --dry-run)

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

---------

Co-authored-by: Krish Garg <krish.garg@rocketride.ai>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Dylan Savage <dylanmsavage@hotmail.com>

* docs: restyle the site, restructure the IA, and finish the content revamp (#2138)

* feat(docs): restyle the docs site to the RocketRide marketing design language

Port the marketing/cloud landing look onto the Docusaurus theme: the
red-orange star (#F93822) as sole accent over a warm paper field with a
dot grid (light) or the Abyss Deep near-black (dark); Ubuntu Mono
uppercase kickers; squared surfaces with hairline borders; contrast-
inversion hovers on cards and sidebar rows; a hard-orange-offset CTA
block; a floating form-fitting dark sidebar panel; and a rocket-mark
"you are here" indicator on the page TOC. Rebuild the homepage as a
Dify-style hero + numbered card sections using new global Card/CardGrid
MDX components.

Cascade notes: the build injects Infima/theme rules at (2,x,y) hacked
specificity and wraps CSS in cascade layers, so overrides variously
match the :not(#\#) hack, exceed it, or pin with !important — each case
is documented inline in custom.css.

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

* fix(docs): make sidebar group kickers readable in the mobile drawer

The sidebar menu markup is reused by the mobile navbar drawer, whose
surface is light — the white kicker text scoped to the menu vanished
there. Kickers now default to the star orange (readable on the drawer
in both themes); only the dark desktop panel re-whitens them.

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

* docs(site): restructure the IA into journey order (phases 1-3)

Rebuild the docs spine around the reader journey — Get Started, Concepts,
Clients, Guides, Examples, Nodes, Connect, Deploy & Operate, Reference,
Support & Community — and make the docs/public/product/ tree, doc ids, and
URLs one namespace again.

- Move 12 pages, split 4 by audience (performance, error-handling, apps,
  cli)…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

builder Node builder tooling and ./builder workflows docs Documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants