feat(landing): per-use-case snippet tabs, connector logo wall, drop asciinema, simplify nav#988
Conversation
…ilding knowledge graph' Verb-led headline with stronger actionability. Adds article 'a' for cleaner grammar. Keeps 'proactive' to differentiate from passive memory stores (memory.store) and preserves 'self-building knowledge graph' as the technical moat. Updates SEO title, meta description, and JSON-LD.
ConnectorRuntime<C, F> now carries checkpoint and config type params.
Defaults to Record<string, unknown> so existing connectors compile unchanged.
New connectors get typed ctx.checkpoint and ctx.config — no more casts:
class MyConnector extends ConnectorRuntime<MyCheckpoint, MyConfig> {
async sync(ctx: SyncContext<MyCheckpoint, MyConfig>) {
const lastSync = ctx.checkpoint?.last_sync_at; // typed!
const label = ctx.config.label; // typed!
}
}
Migrates google_gmail as reference.
Minimal, well-commented example showing the full flow: schema.yaml → watcher (cron + extraction_schema) → reaction script Each file has inline docs explaining the structure. Use as a template for new projects.
Adds ReactionClient interface mirroring the actual isolated-vm proxy
surface (knowledge.save/search/read/delete, entities CRUD, query, log).
Reaction scripts now get full editor autocomplete:
import type { ReactionClient, ReactionContext } from '@lobu/connector-sdk';
export default async (ctx: ReactionContext, client: ReactionClient) => {
await client.knowledge.save({ content: '...', semantic_type: 'digest' });
};
Updates starter, lobu-crm, sales, and finance reaction examples to use
ReactionClient instead of `any`.
The starter example didn't run end-to-end — no connectors, no real data flow. Better to have one real example with great docs than a fake one. - Deletes examples/starter/ (was just a stripped-down stub) - Updates lobu-crm/funnel-form.connector.ts to use generic ConnectorRuntime - Adds lobu-crm/README.md explaining the structure and key files - Existing reaction scripts already use ReactionClient from prior commit
Sections now pull from different examples to show diversity: Connectors: ecommerce/stripe-charges (unchanged) Memory: sales/schema.yaml (unchanged) Watchers: leadership/board-action-tracker (was sales/account-health) Reactions: finance/reconciliation-monitor (was sales/account-health) Agents: lobu-crm/lobu.toml (was sales) Skills: office-bot/deliveroo-order (unchanged) Connectors section: added dotted-underline links to GitHub, Linear, Slack, Telegram, Discord, WhatsApp, X doc pages. Agents section: chat surface names (Slack, Telegram, Discord, Teams, WhatsApp) now link to their channel docs. Watchers section: added separate links for watchers guide and reaction SDK docs.
- per-use-case connector/watcher/memory snippets via tab strip (gen-connectors + simple-icons logo wall) - architecture diagram: Events in/Entities out/Agents on top, per-domain egress judge example - remove asciinema player + casts; em-dash sweep across marketing copy - nav: drop Solutions menu, promote Docs to top level - remove dead /memory /skills stubs, add #memory/#skills section anchors
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughAdds generics to the connector SDK, introduces a typed ReactionClient API, updates example connectors/reactions to use types, implements per-use-case landing snippet generation and UI wiring, removes the asciinema demo, and normalizes landing copy punctuation (no em-dashes). ChangesConnector SDK Type Generics
Reaction Client Type System
Landing Page Use-Case Redesign
Text and Punctuation Normalization
Example Projects and Agent Documentation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
bug_free 82, simplicity 78, slop 12, bugs 1, 0 blockers Script-run typecheck/unit/integration all passed. Exploratory: cd packages/landing && bun run build passed; Astro emitted only the existing CSS @import warning. No server boot, diff is landing/examples/SDK type surface. Suggested fixes
Full verdict JSON{
"bug_free_confidence": 82,
"bugs": 1,
"slop": 12,
"simplicity": 78,
"blockers": [],
"change_type": "feat",
"behavior_change_risk": "low",
"tests_adequate": true,
"suggested_fixes": [
{
"file": "packages/connector-sdk/src/reaction-client-types.ts",
"line": 96,
"change": "Change knowledge.save() to return the actual saveContent shape (id/entity_ids/title/semantic_type/created_at/optional supersedes_event_id/view_url) or Promise<unknown>; event_id is not returned by the runtime."
},
{
"file": "packages/landing/src/components/LandingPage.tsx",
"line": 59,
"change": "Remove the unused linkTabsToCampaigns prop, and remove the corresponding prop passed from packages/landing/src/pages/for/[useCase].astro unless tab navigation is implemented."
}
],
"notes": "Script-run typecheck/unit/integration all passed. Exploratory: cd packages/landing && bun run build passed; Astro emitted only the existing CSS @import warning. No server boot, diff is landing/examples/SDK type surface.",
"categories": {
"src": 1495,
"tests": 0,
"docs": 52,
"config": 9,
"deps": 16,
"migrations": 0,
"ci": 0,
"generated": 1597
}
}Local review gate — branch protection can require the |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/landing/scripts/gen-landing-snippets.ts (1)
562-590:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winScope block-scalar shortening to judge policies only.
Lines 565-590 currently rewrite any
<key>: >/<key>: |scalar, which can unintentionally clobber unrelated frontmatter fields if they use block scalars. Restrict this transform to thenetwork.judgessubtree.🤖 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/landing/scripts/gen-landing-snippets.ts` around lines 562 - 590, The block-scalar shortening currently matches any "<key>: >|"-style scalar via the blockScalar regex (in the block starting with const blockScalar = ...), which can clobber unrelated frontmatter; change the guard so this transform only runs when the current YAML path is inside network.judges. To fix: before applying the rewrite (before using baseIndent/policyName and pushing the folded text), walk upward from the current line i to collect ancestor mapping keys by finding previous non-empty lines with lesser indentation and extracting their keys (use the same key-extraction logic as blockScalar to get a parentKey), build the ancestor path (e.g., "network.judges" if the immediate parent is judges and its parent is network), and only perform the blockScalar rewrite when the built path endsWith "network.judges" (otherwise skip and leave i unchanged). Keep existing symbols: blockScalar, baseIndent, policyName, childPad, and i/j logic; just add the ancestor-path check before mutating out and advancing i.
🤖 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 `@examples/finance/models/reactions/reconciliation-monitor.reaction.ts`:
- Around line 22-25: The issue: windows with only payment_risks are ignored;
update the detection and alert payload to include payment_risks. Modify the
boolean computation in hasIssues (the expression that references
data.unreconciled_count, data.new_variances, data.approaching_deadlines) to also
check (data.payment_risks?.length ?? 0) > 0, and ensure the saved alert
body/metadata (the object constructed where the alert payload is built further
down in the reaction) includes payment_risks so risk-only windows are captured
and persisted; update the analogous checks at the other occurrences mentioned
(the blocks around lines where hasIssues is evaluated and where the alert
body/metadata are assembled).
In `@examples/lobu-crm/README.md`:
- Around line 8-28: The fenced code block in the README.md directory listing is
missing a language specifier; update the opening triple backticks for the block
that begins with "lobu-crm/" to include a language identifier such as text or
plaintext (e.g., change ``` to ```text) so the directory tree renders correctly;
locate the block in examples/lobu-crm/README.md and modify the opening fence
accordingly.
In `@packages/landing/scripts/gen-landing-snippets.ts`:
- Around line 672-678: The findConnectorFile function currently picks the first
file matching *.connector.ts which can be nondeterministic; instead read all
matches using readdirSync(connectorsDir).filter(f =>
f.endsWith(".connector.ts")), then if the resulting array length !== 1 throw a
clear Error (include connectorsDir and the list or count) to fail fast; when
exactly one match exists, return the same { abs, rel } values as before using
that single filename.
In `@packages/landing/src/chat-scenarios.ts`:
- Line 108: The bot copy for the settings link in
packages/landing/src/chat-scenarios.ts is a broken sentence; update the object
property text (the "text" value used for the settings-link) to a single clear
sentence such as: "You can change the model from Settings — this opens a scoped
page with your current agent configuration." Replace the existing malformed
string with this corrected sentence so the in-chat message reads clearly.
---
Outside diff comments:
In `@packages/landing/scripts/gen-landing-snippets.ts`:
- Around line 562-590: The block-scalar shortening currently matches any "<key>:
>|"-style scalar via the blockScalar regex (in the block starting with const
blockScalar = ...), which can clobber unrelated frontmatter; change the guard so
this transform only runs when the current YAML path is inside network.judges. To
fix: before applying the rewrite (before using baseIndent/policyName and pushing
the folded text), walk upward from the current line i to collect ancestor
mapping keys by finding previous non-empty lines with lesser indentation and
extracting their keys (use the same key-extraction logic as blockScalar to get a
parentKey), build the ancestor path (e.g., "network.judges" if the immediate
parent is judges and its parent is network), and only perform the blockScalar
rewrite when the built path endsWith "network.judges" (otherwise skip and leave
i unchanged). Keep existing symbols: blockScalar, baseIndent, policyName,
childPad, and i/j logic; just add the ancestor-path check before mutating out
and advancing i.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 23ebe8fd-c36a-4555-a06d-19fea4ec0f06
⛔ Files ignored due to path filters (7)
bun.lockis excluded by!**/*.lockpackages/landing/public/asciinema-player/asciinema-player-ui.min.jsis excluded by!**/*.min.jspackages/landing/public/asciinema-player/asciinema-player-worker.min.jsis excluded by!**/*.min.jspackages/landing/public/asciinema-player/asciinema-player.min.jsis excluded by!**/*.min.jspackages/landing/src/generated/connectors.jsonis excluded by!**/generated/**packages/landing/src/generated/landing-snippets.jsonis excluded by!**/generated/**packages/landing/src/generated/use-case-models.tsis excluded by!**/generated/**
📒 Files selected for processing (37)
AGENTS.mdexamples/finance/models/reactions/reconciliation-monitor.reaction.tsexamples/lobu-crm/README.mdexamples/lobu-crm/connectors/funnel-form.connector.tsexamples/lobu-crm/models/reactions/inbound-triage.reaction.tsexamples/office-bot/agents/food-ordering/skills/deliveroo-order/SKILL.mdexamples/office-bot/lobu.tomlexamples/sales/models/reactions/account-health-monitor.reaction.tspackages/connector-sdk/src/connector-runtime.tspackages/connector-sdk/src/connector-types.tspackages/connector-sdk/src/index.tspackages/connector-sdk/src/reaction-client-types.tspackages/connectors/src/google_gmail.tspackages/landing/package.jsonpackages/landing/public/asciinema-player/asciinema-player.csspackages/landing/public/casts/claude.castpackages/landing/scripts/gen-connectors.tspackages/landing/scripts/gen-landing-snippets.tspackages/landing/src/chat-scenarios.tspackages/landing/src/components/ArchitectureDiagram.tsxpackages/landing/src/components/CodeBlock.tsxpackages/landing/src/components/LandingPage.tsxpackages/landing/src/components/Nav.tsxpackages/landing/src/components/ProvidersRegistryTable.tsxpackages/landing/src/components/SampleChat.tsxpackages/landing/src/components/ServerlessSection.tsxpackages/landing/src/components/connect-from/TryItSection.tsxpackages/landing/src/connect-from-config.tspackages/landing/src/layouts/BaseLayout.astropackages/landing/src/pages/for/[useCase].astropackages/landing/src/pages/index.astropackages/landing/src/pages/memory.astropackages/landing/src/pages/reference/api-reference.astropackages/landing/src/pages/serverless-openclaw.astropackages/landing/src/pages/skills.astropackages/landing/src/styles/theme-tokens.tspackages/landing/src/use-case-showcases.ts
💤 Files with no reviewable changes (4)
- packages/landing/src/pages/skills.astro
- packages/landing/src/pages/memory.astro
- packages/landing/public/asciinema-player/asciinema-player.css
- packages/landing/src/pages/for/[useCase].astro
| const hasIssues = | ||
| data.unreconciled_count > 0 || | ||
| (data.new_variances?.length ?? 0) > 0 || | ||
| (data.approaching_deadlines?.length ?? 0) > 0; |
There was a problem hiding this comment.
Include payment_risks in issue detection and alert payload.
payment_risks is part of ReconciliationData but is ignored in Line 22-Line 25 and in the saved alert body/metadata, so risk-only windows can be silently dropped.
Suggested fix
const hasIssues =
data.unreconciled_count > 0 ||
(data.new_variances?.length ?? 0) > 0 ||
- (data.approaching_deadlines?.length ?? 0) > 0;
+ (data.approaching_deadlines?.length ?? 0) > 0 ||
+ (data.payment_risks?.length ?? 0) > 0;
@@
if (data.approaching_deadlines?.length) {
parts.push(`Deadlines: ${data.approaching_deadlines.join("; ")}`);
}
+ if (data.payment_risks?.length) {
+ parts.push(`Payment risks: ${data.payment_risks.join("; ")}`);
+ }
@@
unreconciled_count: data.unreconciled_count,
variance_count: data.new_variances?.length ?? 0,
+ payment_risk_count: data.payment_risks?.length ?? 0,
},
});Also applies to: 36-38, 44-48
🤖 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 `@examples/finance/models/reactions/reconciliation-monitor.reaction.ts` around
lines 22 - 25, The issue: windows with only payment_risks are ignored; update
the detection and alert payload to include payment_risks. Modify the boolean
computation in hasIssues (the expression that references
data.unreconciled_count, data.new_variances, data.approaching_deadlines) to also
check (data.payment_risks?.length ?? 0) > 0, and ensure the saved alert
body/metadata (the object constructed where the alert payload is built further
down in the reaction) includes payment_risks so risk-only windows are captured
and persisted; update the analogous checks at the other occurrences mentioned
(the blocks around lines where hasIssues is evaluated and where the alert
body/metadata are assembled).
| ``` | ||
| lobu-crm/ | ||
| ├── lobu.toml # Agent + memory config | ||
| ├── connectors/ | ||
| │ ├── github.yaml # Built-in connector (just config) | ||
| │ ├── x.yaml # Built-in connector | ||
| │ ├── hackernews.yaml # Built-in connector | ||
| │ ├── changelog-watch.yaml # Built-in connector (website) | ||
| │ ├── funnel-form.yaml # Custom connector manifest | ||
| │ └── funnel-form.connector.ts # Custom connector implementation | ||
| ├── models/ | ||
| │ ├── schema.yaml # Entities, relationships, watchers | ||
| │ └── reactions/ | ||
| │ ├── inbound-triage.reaction.ts # Runs after watcher extraction | ||
| │ └── funnel-digest.reaction.ts # Runs after watcher extraction | ||
| └── agents/crm/ | ||
| ├── SOUL.md # Agent personality | ||
| ├── IDENTITY.md # Agent identity | ||
| ├── USER.md # User context | ||
| └── skills/crm-ops/SKILL.md # Agent skill | ||
| ``` |
There was a problem hiding this comment.
Add language identifier to fenced code block.
The directory structure code block is missing a language specifier. Add text or plaintext after the opening backticks for proper rendering.
📝 Proposed fix
-```
+```text
lobu-crm/
├── lobu.toml # Agent + memory configBased on learnings: Static analysis flagged this as a markdown best practice violation.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| lobu-crm/ | |
| ├── lobu.toml # Agent + memory config | |
| ├── connectors/ | |
| │ ├── github.yaml # Built-in connector (just config) | |
| │ ├── x.yaml # Built-in connector | |
| │ ├── hackernews.yaml # Built-in connector | |
| │ ├── changelog-watch.yaml # Built-in connector (website) | |
| │ ├── funnel-form.yaml # Custom connector manifest | |
| │ └── funnel-form.connector.ts # Custom connector implementation | |
| ├── models/ | |
| │ ├── schema.yaml # Entities, relationships, watchers | |
| │ └── reactions/ | |
| │ ├── inbound-triage.reaction.ts # Runs after watcher extraction | |
| │ └── funnel-digest.reaction.ts # Runs after watcher extraction | |
| └── agents/crm/ | |
| ├── SOUL.md # Agent personality | |
| ├── IDENTITY.md # Agent identity | |
| ├── USER.md # User context | |
| └── skills/crm-ops/SKILL.md # Agent skill | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 8-8: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 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 `@examples/lobu-crm/README.md` around lines 8 - 28, The fenced code block in
the README.md directory listing is missing a language specifier; update the
opening triple backticks for the block that begins with "lobu-crm/" to include a
language identifier such as text or plaintext (e.g., change ``` to ```text) so
the directory tree renders correctly; locate the block in
examples/lobu-crm/README.md and modify the opening fence accordingly.
| function findConnectorFile(slug: string): { abs: string; rel: string } { | ||
| const connectorsDir = resolve(examplesDir, slug, "connectors"); | ||
| const file = readdirSync(connectorsDir).find((f) => | ||
| f.endsWith(".connector.ts") | ||
| ); | ||
| if (!file) throw new Error(`No *.connector.ts in ${connectorsDir}`); | ||
| return { |
There was a problem hiding this comment.
Enforce exactly one connector file per use-case slug.
At Line 674, selecting the first *.connector.ts match is implicit and can become nondeterministic if a second connector file appears. Fail fast when count is not exactly one.
Suggested fix
function findConnectorFile(slug: string): { abs: string; rel: string } {
const connectorsDir = resolve(examplesDir, slug, "connectors");
- const file = readdirSync(connectorsDir).find((f) =>
- f.endsWith(".connector.ts")
- );
- if (!file) throw new Error(`No *.connector.ts in ${connectorsDir}`);
+ const files = readdirSync(connectorsDir)
+ .filter((f) => f.endsWith(".connector.ts"))
+ .sort();
+ if (files.length !== 1) {
+ throw new Error(
+ `Expected exactly 1 *.connector.ts in ${connectorsDir}, found ${files.length}`
+ );
+ }
+ const file = files[0];
return {
abs: resolve(connectorsDir, file),
rel: `connectors/${file}`,
};
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function findConnectorFile(slug: string): { abs: string; rel: string } { | |
| const connectorsDir = resolve(examplesDir, slug, "connectors"); | |
| const file = readdirSync(connectorsDir).find((f) => | |
| f.endsWith(".connector.ts") | |
| ); | |
| if (!file) throw new Error(`No *.connector.ts in ${connectorsDir}`); | |
| return { | |
| function findConnectorFile(slug: string): { abs: string; rel: string } { | |
| const connectorsDir = resolve(examplesDir, slug, "connectors"); | |
| const files = readdirSync(connectorsDir) | |
| .filter((f) => f.endsWith(".connector.ts")) | |
| .sort(); | |
| if (files.length !== 1) { | |
| throw new Error( | |
| `Expected exactly 1 *.connector.ts in ${connectorsDir}, found ${files.length}` | |
| ); | |
| } | |
| const file = files[0]; | |
| return { | |
| abs: resolve(connectorsDir, file), | |
| rel: `connectors/${file}`, | |
| }; | |
| } |
🤖 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/landing/scripts/gen-landing-snippets.ts` around lines 672 - 678, The
findConnectorFile function currently picks the first file matching
*.connector.ts which can be nondeterministic; instead read all matches using
readdirSync(connectorsDir).filter(f => f.endsWith(".connector.ts")), then if the
resulting array length !== 1 throw a clear Error (include connectorsDir and the
list or count) to fail fast; when exactly one match exists, return the same {
abs, rel } values as before using that single filename.
| { | ||
| role: "bot", | ||
| text: "You can change the model from your settings — opens a scoped page with your current agent config.", | ||
| text: "You can change the model from your settings, opens a scoped page with your current agent config.", |
There was a problem hiding this comment.
Fix the settings-link bot copy grammar.
Line 108 reads as a broken sentence and is hard to parse in-chat.
Suggested copy fix
- text: "You can change the model from your settings, opens a scoped page with your current agent config.",
+ text: "You can change the model from your settings. It opens a scoped page with your current agent config.",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| text: "You can change the model from your settings, opens a scoped page with your current agent config.", | |
| text: "You can change the model from your settings. It opens a scoped page with your current agent config.", |
🤖 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/landing/src/chat-scenarios.ts` at line 108, The bot copy for the
settings link in packages/landing/src/chat-scenarios.ts is a broken sentence;
update the object property text (the "text" value used for the settings-link) to
a single clear sentence such as: "You can change the model from Settings — this
opens a scoped page with your current agent configuration." Replace the existing
malformed string with this corrected sentence so the in-chat message reads
clearly.
- drop unused linkTabsToCampaigns prop (LandingPage + [useCase].astro)
- reaction save() returns Promise<unknown> (runtime does not return {event_id})
- fix /docs/reference -> /reference link prefix in sync-from-github
- add orphaned lobu-apply doc to the Reference sidebar
Dev-focused landing rework.
Highlights
Validation
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Style