feat: Add zaid-booking-agent kit — multi-agent local service booking system#207
feat: Add zaid-booking-agent kit — multi-agent local service booking system#207Zaid06Khan wants to merge 20 commits into
Conversation
…p stubs Sets up the repo structure for the 4-agent local service booking system (Intake -> Scheduling -> Confirmation -> Follow-up) before any flow logic is built. Flow files are documented stubs awaiting export from Lamatic Studio; docs, prompts, mock availability data, and the Next.js app shell are real content since they don't depend on Studio.
…havior The extraction schema has no nullable type, so the prompt now asks for empty strings instead of null (the model was improvising sentinel text like "<UNKNOWN>" otherwise). Also documents how the API Response node merges the two Condition branches by referencing one source node per field, relying on unexecuted nodes resolving to falsy rather than erroring.
…e merge, codeNode chip fix Syncs local docs/prompts/scripts with the Scheduling Agent flow now built and tested end-to-end in Lamatic Studio (both available/unavailable branches verified).
…ne path Syncs local docs/prompts with the Confirmation Agent flow now built and tested end-to-end in Lamatic Studio (both booked/decline branches verified). Completes the 3-agent MVP: Intake, Scheduling, and Confirmation are all built and tested.
…PI routes Wires up the 3-flow pipeline end-to-end at the app layer: lazy-init Lamatic SDK client, in-memory session store, intake/scheduling/confirmation route handlers, and a working chat UI. Verified via npm run build and a live browser smoke test (error handling correctly surfaces when flow IDs aren't configured yet).
Replaces the doc-only flow stubs with the real, verified node graphs exported via Studio's "Export as AgentKit" menu (Intake, Scheduling, Confirmation). Adds a new model-configs/ directory and adopts Studio's canonical filenames for prompts/ and scripts/, removing the earlier hand-named guesses that predated any real export. All three flow files type-check cleanly.
Confirmed via .github/workflows/update-registry.yml, recent merged kit PR history (e.g. Lamatic#179), and CONTRIBUTING.md's own description of the file.
:robot_face: AgentKit Structural ValidationNew Contributions Detected
Check Results
🎉 All checks passed! This contribution follows the AgentKit structure. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds a Local Service Booking Agent kit with three Lamatic MVP flows, an optional follow-up specification, a Next.js chat application, in-memory session orchestration, environment configuration, prompts, scripts, and setup documentation. ChangesBooking kit contracts and setup
Lamatic flow implementation
Application foundation
API orchestration
Booking chat interface
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
🤖 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 `@kits/zaid-booking-agent/agent.md`:
- Around line 104-106: The booking flow description incorrectly claims that the
`Write Booking` re-check prevents double booking. Either implement an atomic
shared reservation or conditional write for `Write Booking`, or revise the
documentation to state that the MVP does not support cross-session exclusivity
and the re-check cannot prevent concurrent bookings.
In `@kits/zaid-booking-agent/apps/app/page.tsx`:
- Around line 32-44: Update runScheduling so phase is set to
"awaiting_confirmation" only when data.proposed_slots contains at least one
slot; when no slots are returned, set phase to "intake" after preserving the
assistant message, allowing the user to submit a new request. Ensure this
behavior also applies when retrying after a failed confirmation.
In `@kits/zaid-booking-agent/apps/lib/lamatic-client.ts`:
- Around line 5-18: The getClient function does not validate the required
LAMATIC_PROJECT_ID environment variable. Include projectId in the
missing-credentials guard before constructing Lamatic, and update the error
message to clearly mention all three required variables.
In `@kits/zaid-booking-agent/apps/lib/session-store.ts`:
- Around line 1-58: Add bounded session retention to the module-level sessions
store: track each session’s last access or update time, expire stale entries via
a TTL sweep, and enforce a maximum entry count by evicting the oldest sessions.
Update getSession, getOrCreateSession, and updateSession so reads and writes
refresh retention metadata while preserving the existing Session API.
In `@kits/zaid-booking-agent/apps/package.json`:
- Around line 11-25: Add react-hook-form, zod, and lucide-react to the app
dependencies, then update the intake form and booking controls to use
react-hook-form with a zodResolver-backed Zod schema and lucide-react icons.
In `@kits/zaid-booking-agent/docs/decision-log.md`:
- Line 6: Normalize all decision headings in the decision log by changing each
heading beneath “# Decision Log” to level-two “##” headings, including the
entries identified in the review, and insert a blank line after every heading.
In `@kits/zaid-booking-agent/flows/followup-agent.ts`:
- Around line 1-29: The follow-up specification is an invalid flow stub because
it exports no flow metadata or graph structure. Move the documentation from the
`followup-agent.ts` flow file into `docs/`, or replace it with a valid flow
export containing the required metadata, inputs, references, nodes, and edges so
flow discovery can process it correctly.
In `@kits/zaid-booking-agent/lamatic.config.ts`:
- Around line 2-4: The public description in the lamatic configuration currently
claims four implemented agents. Update the description value in the
configuration metadata to describe the implemented three-agent MVP and
explicitly identify the follow-up agent as an optional, unimplemented
specification.
In `@kits/zaid-booking-agent/README.md`:
- Line 22: The README setup guide has Markdown lint issues around its fenced
code blocks. Add an appropriate language identifier to the repository-layout
fence, and ensure blank lines surround the Bash fence at the referenced
sections.
In `@kits/zaid-booking-agent/scripts/mock-availability.js`:
- Around line 45-47: Update getNearbySlots to select OPEN_SLOTS based on the
requested date and return the nearest available alternatives, passing the
requested date from its callers; alternatively, explicitly document and expose
that this fixture helper only returns the first entries so callers do not imply
date-aware behavior.
- Around line 7-9: The swap note is attached to unused mock code while the live
scheduling flow filters OPEN_SLOTS inline in
scheduling-agent_code-node-970_code.ts. Update the note to identify that code
node as the replacement point, or refactor the node to call getAvailability() so
replacing that function with a calendar provider updates the live flow.
In `@kits/zaid-booking-agent/scripts/scheduling-agent_code-node-970_code.ts`:
- Line 15: The nearby slot selection currently takes the first three entries
rather than choosing alternatives closest to the requested date. Update the
logic around `output.nearby_slots` and `OPEN_SLOTS` to parse `requestedDate`,
rank available slots by absolute date distance (with a deterministic
tie-breaker), and return the three nearest slots while preserving the expected
slot structure.
- Around line 11-14: Update the scheduling logic using requestedDate and
preferred_window so OPEN_SLOTS are filtered by both matching date and a
normalized time-window value; normalize the request and each slot’s window
consistently before comparison, then derive output.slot_available and
output.open_slots from that combined filter rather than all same-day slots.
🪄 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: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: cd79da26-a221-432a-a0fe-e33b932d4e9e
⛔ Files ignored due to path filters (7)
kits/zaid-booking-agent/apps/app/favicon.icois excluded by!**/*.icokits/zaid-booking-agent/apps/package-lock.jsonis excluded by!**/package-lock.jsonkits/zaid-booking-agent/apps/public/file.svgis excluded by!**/*.svgkits/zaid-booking-agent/apps/public/globe.svgis excluded by!**/*.svgkits/zaid-booking-agent/apps/public/next.svgis excluded by!**/*.svgkits/zaid-booking-agent/apps/public/vercel.svgis excluded by!**/*.svgkits/zaid-booking-agent/apps/public/window.svgis excluded by!**/*.svg
📒 Files selected for processing (44)
kits/zaid-booking-agent/.env.examplekits/zaid-booking-agent/.gitignorekits/zaid-booking-agent/README.mdkits/zaid-booking-agent/agent.mdkits/zaid-booking-agent/apps/.env.examplekits/zaid-booking-agent/apps/.gitignorekits/zaid-booking-agent/apps/app/api/confirmation/route.tskits/zaid-booking-agent/apps/app/api/intake/route.tskits/zaid-booking-agent/apps/app/api/scheduling/route.tskits/zaid-booking-agent/apps/app/api/session/[id]/route.tskits/zaid-booking-agent/apps/app/globals.csskits/zaid-booking-agent/apps/app/layout.tsxkits/zaid-booking-agent/apps/app/page.tsxkits/zaid-booking-agent/apps/eslint.config.mjskits/zaid-booking-agent/apps/lib/lamatic-client.tskits/zaid-booking-agent/apps/lib/session-store.tskits/zaid-booking-agent/apps/next.config.tskits/zaid-booking-agent/apps/package.jsonkits/zaid-booking-agent/apps/postcss.config.mjskits/zaid-booking-agent/apps/tsconfig.jsonkits/zaid-booking-agent/constitutions/default.mdkits/zaid-booking-agent/docs/decision-log.mdkits/zaid-booking-agent/flows/confirmation-agent.tskits/zaid-booking-agent/flows/followup-agent.tskits/zaid-booking-agent/flows/intake-agent.tskits/zaid-booking-agent/flows/scheduling-agent.tskits/zaid-booking-agent/lamatic.config.tskits/zaid-booking-agent/model-configs/confirmation-agent_llmnode-440_generative-model-name.tskits/zaid-booking-agent/model-configs/intake-agent_instructor-llmnode-254_generative-model-name.tskits/zaid-booking-agent/model-configs/scheduling-agent_llmnode-969_generative-model-name.tskits/zaid-booking-agent/prompts/confirmation-agent_llmnode-440_system_0.mdkits/zaid-booking-agent/prompts/confirmation-agent_llmnode-440_user_1.mdkits/zaid-booking-agent/prompts/followup-agent_reminder_system.mdkits/zaid-booking-agent/prompts/intake-agent_instructor-llmnode-254_system_0.mdkits/zaid-booking-agent/prompts/intake-agent_instructor-llmnode-254_user_1.mdkits/zaid-booking-agent/prompts/scheduling-agent_llmnode-969_system_0.mdkits/zaid-booking-agent/prompts/scheduling-agent_llmnode-969_user_1.mdkits/zaid-booking-agent/scripts/confirmation-agent_code-node-672_code.tskits/zaid-booking-agent/scripts/confirmation-agent_code-node-676_code.tskits/zaid-booking-agent/scripts/intake-agent_code-node-677_code.tskits/zaid-booking-agent/scripts/intake-agent_code-node-969_code.tskits/zaid-booking-agent/scripts/mock-availability.jskits/zaid-booking-agent/scripts/scheduling-agent_code-node-594_code.tskits/zaid-booking-agent/scripts/scheduling-agent_code-node-970_code.ts
| const requestedDate = {{triggerNode_1.output.preferred_date}}; | ||
| const sameDaySlots = OPEN_SLOTS.filter((slot) => slot.date === requestedDate); | ||
| output.slot_available = sameDaySlots.length > 0; | ||
| output.open_slots = sameDaySlots; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Match the requested time window, not just the date.
The scheduling flow receives preferred_window, but this node ignores it and marks the entire day available whenever any same-day slot exists. A request for an unavailable window can therefore be confirmed with unrelated times. Filter by both date and normalized window before setting slot_available.
🧰 Tools
🪛 Biome (2.5.1)
[error] 11-11: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '{triggerNode_1.output.preferred_date'.
(parse)
[error] 11-11: Expected a statement but instead found '}'.
(parse)
🤖 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 `@kits/zaid-booking-agent/scripts/scheduling-agent_code-node-970_code.ts`
around lines 11 - 14, Update the scheduling logic using requestedDate and
preferred_window so OPEN_SLOTS are filtered by both matching date and a
normalized time-window value; normalize the request and each slot’s window
consistently before comparison, then derive output.slot_available and
output.open_slots from that combined filter rather than all same-day slots.
|
Hi @Zaid06Khan! 👋 Before this PR can be reviewed by maintainers, please resolve all comments and requested changes from the CodeRabbit automated review. Steps to follow:
This helps keep the review process efficient for everyone. Thank you! 🙏 |
CodeRabbit review: nearby_slots always returned the first 3 OPEN_SLOTS entries regardless of the requested date, so "nearest alternatives" for a far-off date incorrectly offered the earliest slots in the mock data. Now sorts by absolute date distance from the requested date. Fixed in both the live Studio codeNode (tested and redeployed) and its local mirror, plus the two doc references describing the old behavior.
CodeRabbit review: runScheduling() always set phase to "awaiting_confirmation" after a Scheduling Agent call, even when proposed_slots came back empty on the unavailable-date branch. With no slot buttons to click and the input disabled, the user was stranded — only escape was a page refresh, which lost the session. Now falls back to phase "intake" when there are no slots, re-enabling the input so the customer can request a different date. Verified live against the deployed flows alongside the nearby_slots fix.
CodeRabbit review: the guard only checked endpoint and apiKey, but projectId is also required by the SDK constructor. Missing it now fails fast with a clear error instead of a confusing SDK-level one.
CodeRabbit review: flows/ is reserved for real Studio flow exports (meta/inputs/references/nodes/edges) per CLAUDE.md, and followup-agent.ts was a docs-only stub with none of that — flow-discovery tooling could choke on it as a malformed flow. Moved the same content, unchanged, to docs/followup-agent-spec.md and cross-referenced it from agent.md.
CodeRabbit review: "guards against a double-booking race" overstated what the re-check actually does — it reads the same static mock array with no atomic reservation or persistence, so two near-simultaneous confirmations can still both pass. Reworded to match the honest framing already in this doc's own "Known limitation" bullet a few lines down.
Same overclaiming phrase as agent.md's Write Booking description
("guard against a double-booking race... near-simultaneously") was
duplicated in this file's own header comment. Reworded for consistency
with the previous commit and the file's own Known limitation section.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
kits/zaid-booking-agent/apps/app/page.tsx (2)
77-108: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winAgent, the confirmation retry has a trap door that locks from the outside.
When
handleConfirmSlotclearsproposedSlots(line 87) before the async confirmation call, any failure downstream leaves the user in a dead-end state:
- Confirmation API fails (line 96 throws): phase is still
"awaiting_confirmation"from the original scheduling, slots are empty, input is disabled (phase !== "intake") — user is stuck.- Confirmation returns
!data.bookedandrunScheduling()throws (line 102):setPhase("awaiting_confirmation")on line 99 takes effect,runScheduling()never gets to override it, slots are empty — user is stuck.In both cases, the error message renders but the user has no recovery path short of a page refresh, which destroys the session.
Fix: reset phase to
"intake"in the catch block so the user can type a new request.🔧 Proposed fix — add error recovery to `handleConfirmSlot`
} catch (err) { setError(err instanceof Error ? err.message : "Something went wrong"); + setPhase("intake"); } finally { setLoading(false); }🤖 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 `@kits/zaid-booking-agent/apps/app/page.tsx` around lines 77 - 108, handleConfirmSlot leaves the UI with no selectable slots after confirmation failures. In its catch block, call setPhase("intake") alongside setting the error so the user can enter a new request and recover without refreshing.
111-183: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffAlign this screen with the kit UI/form stack
The transcript/slot area still uses raw HTML controls, and the intake flow is wired withuseState. To match the kit conventions, move the controls to shadcn/ui primitives and switch the form toreact-hook-form+zod.🤖 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 `@kits/zaid-booking-agent/apps/app/page.tsx` around lines 111 - 183, The booking screen still uses native controls and useState for intake; refactor the component’s form flow to react-hook-form with a zod schema and replace the input, buttons, and transcript/slot presentation controls with the project’s shadcn/ui primitives. Preserve existing phase, loading, validation, submission, and slot-confirmation behavior while using the kit components consistently.Source: Coding guidelines
🤖 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.
Outside diff comments:
In `@kits/zaid-booking-agent/apps/app/page.tsx`:
- Around line 77-108: handleConfirmSlot leaves the UI with no selectable slots
after confirmation failures. In its catch block, call setPhase("intake")
alongside setting the error so the user can enter a new request and recover
without refreshing.
- Around line 111-183: The booking screen still uses native controls and
useState for intake; refactor the component’s form flow to react-hook-form with
a zod schema and replace the input, buttons, and transcript/slot presentation
controls with the project’s shadcn/ui primitives. Preserve existing phase,
loading, validation, submission, and slot-confirmation behavior while using the
kit components consistently.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: e21526da-a67b-4712-9ecf-cfd16f518d6c
📒 Files selected for processing (8)
kits/zaid-booking-agent/agent.mdkits/zaid-booking-agent/apps/app/page.tsxkits/zaid-booking-agent/apps/lib/lamatic-client.tskits/zaid-booking-agent/docs/followup-agent-spec.mdkits/zaid-booking-agent/flows/confirmation-agent.tskits/zaid-booking-agent/flows/scheduling-agent.tskits/zaid-booking-agent/scripts/mock-availability.jskits/zaid-booking-agent/scripts/scheduling-agent_code-node-970_code.ts
|
Re: the "Align this screen with the kit UI/form stack" suggestion on Same reasoning as the earlier Leaving this as-is for the same reason CodeRabbit already agreed to on the dependency thread. Happy to revisit if the UI grows real form fields later. |
CodeRabbit review (post-push): same stuck-UI bug class as the earlier runScheduling() fix, but in the confirm path. If the confirmation call throws, or the declined-slot retry's runScheduling() call throws, phase stayed on "awaiting_confirmation" with no slots to click and a disabled input. Catch block now resets phase to "intake" so the customer can type a new request instead of needing a page refresh.
- decision-log.md: bump ### headings to ## with blank lines after, matching the file's h1 - README.md: tag the repo-layout fence as text - lamatic.config.ts: description now reflects the 3-agent MVP, Follow-up as unbuilt stretch goal - mock-availability.js: point the "swap in a real calendar" note at the actual live codeNode - session-store.ts: add a 24h TTL with lazy eviction so the in-memory session Map doesn't grow unbounded Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
kits/zaid-booking-agent/apps/app/page.tsx (2)
126-156: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winScreen readers won't hear incoming transmissions.
The chat transcript and slot buttons have no
aria-liveregion, so assistive technology users won't be notified when new messages or slots appear. Addaria-live="polite"to the message container so dynamic content is announced.♿ Suggested accessibility improvement
- <main className="flex-1 space-y-3 overflow-y-auto p-4"> + <main aria-live="polite" className="flex-1 space-y-3 overflow-y-auto p-4">🤖 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 `@kits/zaid-booking-agent/apps/app/page.tsx` around lines 126 - 156, Add aria-live="polite" to the main chat transcript container surrounding the messages and proposed slot buttons, ensuring dynamically added messages and slots are announced to screen readers without interrupting the user.
126-138: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAgent, your operatives won't see the latest intel without scrolling.
New messages are appended to the DOM but the
overflow-y-autocontainer never auto-scrolls to the bottom. After each assistant response or slot render, the user must manually scroll to see the latest message — a friction point in any chat UX.Consider a bottom ref +
useEffectthat scrolls into view onmessagesorproposedSlotschange.♻️ Optional auto-scroll refactor
+ const bottomRef = useRef<HTMLDivElement>(null); + + useEffect(() => { + bottomRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages, proposedSlots]); + return ( <div className="mx-auto flex min-h-screen w-full max-w-lg flex-col bg-zinc-50 dark:bg-black"> {/* ... */} <main className="flex-1 space-y-3 overflow-y-auto p-4"> {/* ... existing messages and slots ... */} + <div ref={bottomRef} /> </main>🤖 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 `@kits/zaid-booking-agent/apps/app/page.tsx` around lines 126 - 138, Chat messages do not automatically scroll to the newest content. In the page component, add a bottom sentinel ref and an effect keyed to messages and proposedSlots that scrolls the sentinel into view after updates; render the sentinel at the end of the overflow-y-auto message container.
🤖 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.
Outside diff comments:
In `@kits/zaid-booking-agent/apps/app/page.tsx`:
- Around line 126-156: Add aria-live="polite" to the main chat transcript
container surrounding the messages and proposed slot buttons, ensuring
dynamically added messages and slots are announced to screen readers without
interrupting the user.
- Around line 126-138: Chat messages do not automatically scroll to the newest
content. In the page component, add a bottom sentinel ref and an effect keyed to
messages and proposedSlots that scrolls the sentinel into view after updates;
render the sentinel at the end of the overflow-y-auto message container.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: f2e469b5-4750-4687-8d97-4c8202a50520
📒 Files selected for processing (6)
kits/zaid-booking-agent/README.mdkits/zaid-booking-agent/apps/app/page.tsxkits/zaid-booking-agent/apps/lib/session-store.tskits/zaid-booking-agent/docs/decision-log.mdkits/zaid-booking-agent/lamatic.config.tskits/zaid-booking-agent/scripts/mock-availability.js
CodeRabbit flagged both as trivial UX/a11y gaps on page.tsx: screen readers weren't notified of new messages/slots, and the transcript never scrolled to show the latest content automatically. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
kits/zaid-booking-agent/apps/app/page.tsx (2)
173-181: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winAgent, your input is undercover — no label, no
aria-label. Screen readers won't announce its purpose.The placeholder text is not a substitute for an accessible label. Add an
aria-labelto the input element.🔧 Proposed fix
<input value={input} onChange={(e) => setInput(e.target.value)} disabled={loading || phase !== "intake"} + aria-label="Chat message" placeholder={🤖 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 `@kits/zaid-booking-agent/apps/app/page.tsx` around lines 173 - 181, Add an explicit aria-label to the input element in the chat input form, alongside its existing value, onChange, disabled, and placeholder props, so screen readers announce its purpose.
38-42: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winAgent, your fetch calls are running dark — no abort signal, no timeout. If a Lamatic flow stalls server-side, the UI freezes indefinitely with no escape hatch.
All three
fetchcalls (lines 38, 62, 95) lack anAbortControlleror timeout. Wrap them with a signal that fires after a reasonable deadline (e.g., 30 s) so the user gets an error instead of an infinite spinner.🔧 Proposed fix — shared timeout helper
+ function fetchWithTimeout(url: string, options: RequestInit = {}, ms = 30000) { + const controller = new AbortController(); + const id = setTimeout(() => controller.abort(), ms); + return fetch(url, { ...options, signal: controller.signal }).finally(() => + clearTimeout(id) + ); + }Then replace each
fetch(...)call:- const res = await fetch("/api/scheduling", { + const res = await fetchWithTimeout("/api/scheduling", {Also applies to: 62-66, 95-99
🤖 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 `@kits/zaid-booking-agent/apps/app/page.tsx` around lines 38 - 42, All three fetch calls in the page component need timeout protection. Add a shared AbortController-based timeout helper, such as a 30-second signal, and pass its signal option to the fetch calls at the scheduling request and the other two request sites; ensure timers are cleaned up and abort errors follow the existing UI error handling.
🤖 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.
Outside diff comments:
In `@kits/zaid-booking-agent/apps/app/page.tsx`:
- Around line 173-181: Add an explicit aria-label to the input element in the
chat input form, alongside its existing value, onChange, disabled, and
placeholder props, so screen readers announce its purpose.
- Around line 38-42: All three fetch calls in the page component need timeout
protection. Add a shared AbortController-based timeout helper, such as a
30-second signal, and pass its signal option to the fetch calls at the
scheduling request and the other two request sites; ensure timers are cleaned up
and abort errors follow the existing UI error handling.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 02f87f63-8cd3-47f5-9860-cf0ec1e13248
📒 Files selected for processing (1)
kits/zaid-booking-agent/apps/app/page.tsx
|
Re: the "fetch calls are running dark — no abort signal, no timeout" finding on Agreed this is a real gap: if a Lamatic flow stalls server-side, the UI currently spins with no escape hatch besides a page refresh. Not fixing it in this PR though — it's out of scope for the 3-agent MVP (flow calls complete in seconds in dev/demo use, and this is a small enough change to bundle into a focused follow-up rather than block this merge on). Logged it as a known deferred item in The |
CodeRabbit's follow-up review flagged the chat input lacking an accessible label (fixed) and the fetch calls lacking a timeout/abort (acknowledged on the PR thread and logged as a deferred known limitation rather than fixed now — out of scope for the 3-agent MVP). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
In Turbopack dev mode, separate app/api/*/route.ts files were getting independent instantiations of session-store.ts's module-level Maps, so a session created by /api/intake was invisible to /api/scheduling and /api/confirmation — reproduced live: GET /api/session/[id] 404'd right after intake had just populated it. Pinning the Maps to globalThis (the same pattern Next recommends for surviving HMR with a Prisma Client) guarantees a single real instance regardless of module re-evaluation. Verified against the running dev server: session state now visible cross-route, and the "session has no structured request yet" error is gone. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Intake's completion check only required service_type, letting requests with no date through as "complete" and handing Scheduling a blank preferred_date. Condition now requires both fields (AND), and the clarifying question asks about whichever is missing. Scheduling's Suggest Alternatives prompt gets a matching guard: a blank date is no longer treated as an "unavailable slot" case, so it returns a clean clarifying question instead of exposing the model's raw reasoning. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ation leak Three more bugs found in live testing: - Intake's Extraction node had no anchor to the real current date, so relative/year-less dates could resolve to the wrong year. Pass a real server-computed `today` through to the prompt. - Scheduling's suggested alternatives were free-text only, so accepting one required typing a new message that re-entered Intake with the whole conversation joined as one string and no precedence rule for conflicting dates — causing a repeat loop back to the original, unavailable date. Check Availability now also populates `proposed_slots` on the unavailable branch (sourced from `nearby_slots`), making alternatives real clickable buttons and removing the free-text round-trip entirely. - Confirmation's message prompt rendered a dangling "for " when the customer name was blank, causing the same reasoning-leak failure mode already fixed once for Scheduling. Same guard pattern applied here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary
Adds
kits/zaid-booking-agent/— a multi-agent appointment booking system for local servicebusinesses (salon, barbershop, and similar). A customer sends a free-text booking request, and
three orchestrated Lamatic flows carry it through extraction, availability checking, and
confirmation, with a Next.js chat demo app driving the conversation.
Architecture
Four discrete flows, each with a typed input/output contract and independently testable in
Lamatic Studio — not one large prompt:
phone, notes) from a raw customer message; asks a clarifying question if anything required
is missing.
the slot or has an LLM propose 2–3 real alternatives (never fabricated).
double-booking race), then generates a natural-language confirmation — or a decline message
if the slot was taken in the meantime.
reminder flow; scoped out to keep the initial submission focused on a fully working
3-agent MVP rather than a partially-working 4-agent one.
Intake, Scheduling, and Confirmation are all built, tested end-to-end in Lamatic Studio (every
branch of every flow verified with real inputs), exported into
flows/*.ts, deployed, andverified against the live Lamatic API through the Next.js app's actual chat UI.
See
agent.mdfor the full per-flow contracts, guardrails, and failure modes, and
docs/decision-log.mdfor the reasoning behind every non-obvious architectural choice (why the Next.js app owns
session state instead of flow-to-flow chaining, why availability data lives inline in a
codeNode, how branch outputs merge in the API Response mapping, etc.).
Test plan
flows/*.tsflows/*.tsandmodel-configs/*.tstype-check cleanly (tsc --noEmit)apps/builds cleanly (npm run build) and boots (npm run dev)through the deployed flows via the chat UI, including the API key rotated afterward
.envfiles, or real credentials committed (apps/.envis gitignored;verified against the full branch diff)
🤖 Generated with Claude Code
kits/zaid-booking-agent/kits/zaid-booking-agent/.env.examplekits/zaid-booking-agent/.gitignorekits/zaid-booking-agent/README.mdkits/zaid-booking-agent/agent.mdkits/zaid-booking-agent/lamatic.config.tskits/zaid-booking-agent/constitutions/default.mdkits/zaid-booking-agent/docs/decision-log.mdkits/zaid-booking-agent/docs/followup-agent-spec.mdkits/zaid-booking-agent/flows/intake-agent.tskits/zaid-booking-agent/flows/scheduling-agent.tskits/zaid-booking-agent/flows/confirmation-agent.tskits/zaid-booking-agent/model-configs/intake-agent_instructor-llmnode-254_generative-model-name.tskits/zaid-booking-agent/model-configs/scheduling-agent_llmnode-969_generative-model-name.tskits/zaid-booking-agent/model-configs/confirmation-agent_llmnode-440_generative-model-name.tskits/zaid-booking-agent/prompts/intake-agent_instructor-llmnode-254_system_0.mdkits/zaid-booking-agent/prompts/intake-agent_instructor-llmnode-254_user_1.mdkits/zaid-booking-agent/prompts/scheduling-agent_llmnode-969_system_0.mdkits/zaid-booking-agent/prompts/scheduling-agent_llmnode-969_user_1.mdkits/zaid-booking-agent/prompts/confirmation-agent_llmnode-440_system_0.mdkits/zaid-booking-agent/prompts/confirmation-agent_llmnode-440_user_1.mdkits/zaid-booking-agent/prompts/followup-agent_reminder_system.mdkits/zaid-booking-agent/scripts/mock-availability.jskits/zaid-booking-agent/scripts/intake-agent_code-node-969_code.tskits/zaid-booking-agent/scripts/intake-agent_code-node-677_code.tskits/zaid-booking-agent/scripts/scheduling-agent_code-node-970_code.tskits/zaid-booking-agent/scripts/scheduling-agent_code-node-594_code.tskits/zaid-booking-agent/scripts/confirmation-agent_code-node-672_code.tskits/zaid-booking-agent/scripts/confirmation-agent_code-node-676_code.tskits/zaid-booking-agent/apps/(Next.js chat demo + API orchestration)kits/zaid-booking-agent/apps/.env.examplekits/zaid-booking-agent/apps/.gitignorekits/zaid-booking-agent/apps/eslint.config.mjskits/zaid-booking-agent/apps/next.config.tskits/zaid-booking-agent/apps/package.jsonkits/zaid-booking-agent/apps/postcss.config.mjskits/zaid-booking-agent/apps/tsconfig.jsonkits/zaid-booking-agent/apps/app/globals.csskits/zaid-booking-agent/apps/app/layout.tsxkits/zaid-booking-agent/apps/app/page.tsxkits/zaid-booking-agent/apps/app/api/intake/route.tskits/zaid-booking-agent/apps/app/api/scheduling/route.tskits/zaid-booking-agent/apps/app/api/confirmation/route.tskits/zaid-booking-agent/apps/app/api/session/[id]/route.tskits/zaid-booking-agent/apps/lib/lamatic-client.tskits/zaid-booking-agent/apps/lib/session-store.tskits/zaid-booking-agent/apps/app/favicon.icoandkits/zaid-booking-agent/apps/public/*.svgflow.jsoncheckflow.jsonfile exists in the repository; flow structure is determined from the exported Lamatic modules inkits/zaid-booking-agent/flows/*.ts.Flow node types introduced (from exported flow modules)
triggerNode(GraphQL API request)InstructorLLMNode(Intake extraction, schema-based structured extraction)LLMNode(Scheduling alternatives + Confirmation copy)codeNode(represented asdynamicNodein exports; e.g., prepare clarification/success, compute availability, validate “booking”)Condition(branching)responseNode(maps node outputs into the flow response payload)High-level flow behavior (what each flow does)
intake-agent.ts){ message, session_id }InstructorLLMNodeto extract a booking request (service_type required; other fields empty-string if missing)Condition: ifservice_typeis empty → returnsneeds_clarification: true+ a fixed clarifying question; otherwise → returnsneeds_clarification: false+ a structuredrequestscheduling-agent.ts){ preferred_date, preferred_window, session_id }(window passed through; availability currently matches by date)codeNodechecks availability against mockOPEN_SLOTS, producing:slot_available,open_slots(same-day matches), andnearby_slots(closest fallbacks)Condition: if slot available → returnsproposed_slots(=open_slots) and a confirmation message; else →LLMNodegenerates a short alternative-suggestion message usingnearby_slotsconfirmation-agent.ts){ confirmed_date, confirmed_time, service_type, customer_name, session_id }codeNodere-checks the selected slot against mock availability and setsbooked: booleanCondition: if booked →LLMNodegenerates a warm confirmation message; else → returns a fixed decline/failure message indicating the slot was takenHow it works end-to-end in the demo