Workshop Series
- Workshop 1: Prompt-First Product Design for a Tagalog Learning App
- Workshop 2: Educational-First Dev Tips for a Tagalog Learning App
- Workshop 3: Deep-Dive Development Flow for a Tagalog Learning App
- Workshop 4: Localize a Tagalog Learning App to Chinese Variants
- Workshop 5: Grammar and Pronunciation Enrichment Pipeline
- Workshop 6: Unique and Reviewable Extra Examples
Audience: professional developers building learning products, internal enablement tools, or community apps
Duration: 2 hours
Primary AWS AI service: Kiro
Project output: an educational-first Tagalog practice app with learning categories, politeness rules, validation helpers, and Kiro-assisted tests.
Workshop Summary

This workshop helps developers design language-learning software around pedagogy before features. Participants use Kiro to capture learning goals, respectful speech rules, practice categories, content review expectations, and tests. The exercises show how educational constraints can guide data models, UI behavior, and automation so a Tagalog practice app remains useful, beginner-friendly, culturally careful, and easy to extend over future content updates.
Workshop objective
This workshop teaches developers how to build a language-learning app that is educational before it is technical. The app focuses on beginner confidence, respectful speech, repeatable practice, pronunciation, and practical event situations. Kiro is used to create specs, guide implementation with steering, generate code, produce tests, and define hooks that keep the educational contract intact.
2-hour agenda
| Time | Module | Developer outcome |
|---|---|---|
| 0–10 min | Educational scope | Non-commercial learning rules defined |
| 10–25 min | Kiro steering | Product, pedagogy, and tech standards saved |
| 25–40 min | Kiro spec | Educational requirements and tasks generated |
| 40–65 min | Practice categories | Category model and card data implemented |
| 65–85 min | Politeness engine | Rule helper chooses safer polite guidance |
| 85–105 min | Practice UI | Prompt/reveal study cards built |
| 105–115 min | Tests and hooks | Validation for review status and tone labels |
| 115–120 min | Wrap-up | Extension roadmap created |
Step 1 — Define educational-first steering
Kiro prompt sample
Create steering docs for an educational Tagalog learning app.
The app must reduce learner fear, normalize mistakes, teach polite speech, and provide short practice drills.
Use React, TypeScript, Vite, Vitest, and local JSON data.
Generated language content is draft until native-speaker reviewed.
System design decision
- Pedagogy as architecture: The product goal is not just translating English into Tagalog. The app must help a beginner practice safely, understand tone, and speak respectfully at a community event. Educational rules belong in steering because they influence schema design, UI layout, testing, and content review.
- Learning safety through constraints: The app should avoid overconfident AI behavior. Review states, short explanations, tone labels, and native-speaker review reminders make the system honest about limitations. This is especially important when teaching language and cultural context.
- Developer repeatability: Professional developers need a repeatable method for creating learning features. Steering docs ensure Kiro uses the same project vocabulary—practice categories, sentence cards, polite forms, pronunciation, examples, and review status—throughout the workshop.
Code sample — .kiro/steering/pedagogy.md
# Educational Guidance
The app teaches beginners by using small, repeatable, respectful practice cards.
Each card should help the learner understand what to say, when to say it, and how formal it sounds.
## Learning rules
- Prefer short examples that can be spoken aloud.
- Show natural Tagalog before playful Filipino-English.
- Mark playful Filipino-English as informal.
- Explain `po`, `opo`, `kayo`, and `ninyo` as respect markers.
- Provide pronunciation near the phrase.
- Use review status: `draft`, `native-reviewed`, or `blocked`.
- Never treat generated language content as final without native-speaker review.
Code explanation
- Business logic: This steering file defines the educational standards that every feature should follow.
- Code logic: Kiro reads the Markdown context and uses it to shape generated requirements, components, tests, and review suggestions.
- Expected result: Generated implementation stays aligned with the learning strategy instead of becoming a generic flashcard app.
Step 2 — Generate an educational Kiro spec
Kiro prompt sample
Create a Kiro spec named educational-practice-flow.
The feature teaches Tagalog through practice categories, prompt/reveal cards, politeness notes, pronunciation, and review status.
Use acceptance criteria that can be tested with Vitest and Testing Library.
System design decision
- Feature-level spec for learning flow: The spec focuses on how learners practice, not only how data renders. This makes the system testable from the learner’s point of view: choose a category, read the English prompt, reveal Tagalog, inspect politeness, and repeat.
- Acceptance criteria drive implementation: Testable criteria reduce ambiguity. If the spec says “WHEN the learner reveals a card THEN polite Tagalog SHALL become visible,” developers can implement and test that behavior directly.
- Small feature slice: A two-hour workshop needs one vertical slice. Category selection, prompt/reveal, and politeness notes are enough to demonstrate Kiro’s spec-driven workflow without introducing authentication, databases, or external APIs.
Code sample — .kiro/specs/educational-practice-flow/tasks.md
# Tasks
- [ ] Define PracticeCategory and PracticeCard types.
- [ ] Add seed cards for greetings, directions, workshops, thanks, and waiting.
- [ ] Implement politeness helper that returns safer guidance for first-time contacts.
- [ ] Build CategorySelector component.
- [ ] Build PracticeCard component with reveal behavior.
- [ ] Add tests for category filtering, reveal behavior, informal labels, and review status.
- [ ] Add hook guidance for running tests after card data changes.
Code explanation
- Business logic: Tasks represent the educational workflow from data to practice behavior.
- Code logic: Each task can be implemented independently and checked off in Kiro.
- Expected result: Developers have a clear route from requirements to working app.
Step 3 — Create practice-category data
Kiro prompt sample
Generate a TypeScript model for Tagalog practice categories and cards.
Include event categories: greetings, directions, workshops, thanks, waiting.
Each card needs English prompt, natural Tagalog, polite Tagalog, tone, pronunciation, and review status.
System design decision
- Categories organize learning intent: Learners prepare for situations, not abstract vocabulary lists. Categories such as greetings, directions, workshops, thanks, and waiting map directly to AWS Manila Community Day actions.
- Prompt/reveal format supports memory: Showing the English prompt first encourages active recall. Revealing Tagalog afterward creates a simple practice loop that can later evolve into spaced repetition.
- Review status travels with content: Because educational quality depends on correctness, each card stores review state. This allows the UI and tests to prevent unreviewed content from being presented as final.
Code sample — src/data/practiceCards.ts
export type PracticeCategory = "greetings" | "directions" | "workshops" | "thanks" | "waiting";
export type ReviewStatus = "draft" | "native-reviewed" | "blocked";
export interface PracticeCard {
id: string;
category: PracticeCategory;
englishPrompt: string;
naturalTagalog: string;
politeTagalog: string;
tone: "casual" | "polite" | "event-ready" | "informal-playful";
pronunciation: string;
reviewStatus: ReviewStatus;
}
export const practiceCards: PracticeCard[] = [
{
id: "greetings-hello-learning",
category: "greetings",
englishPrompt: "Hello, I am learning Tagalog.",
naturalTagalog: "Kumusta, nag-aaral ako ng Tagalog.",
politeTagalog: "Kumusta po, nag-aaral po ako ng Tagalog.",
tone: "event-ready",
pronunciation: "koo-MOOS-tah poh, nag-ah-AH-ral poh AH-koh ngah tah-GAH-log",
reviewStatus: "draft"
},
{
id: "directions-workshop-room",
category: "directions",
englishPrompt: "Where is the workshop room?",
naturalTagalog: "Saan ang workshop room?",
politeTagalog: "Saan po ang workshop room?",
tone: "polite",
pronunciation: "SAH-ahn poh ahng workshop room",
reviewStatus: "draft"
},
{
id: "waiting-on-my-way",
category: "waiting",
englishPrompt: "I am on my way.",
naturalTagalog: "Papunta na ako.",
politeTagalog: "Papunta na po ako.",
tone: "event-ready",
pronunciation: "pah-POON-tah nah poh AH-koh",
reviewStatus: "draft"
}
];
Code explanation
- Business logic: The data prepares learners for real event actions: greet, ask directions, and coordinate arrival.
- Code logic: Union types limit valid categories, tone values, and review states. This prevents accidental free-form labels.
- Expected result: Developers can filter cards by category and render a consistent practice flow.
Step 4 — Implement a politeness guidance helper
Kiro prompt sample
Create a TypeScript helper that explains whether polite Tagalog is safer.
Inputs: audience type and situation.
Return a guidance message, recommended variant, and reason.
System design decision
- Politeness as product logic: Respectful speech is not a side note. The app’s core value is helping a beginner choose safer wording with speakers, organizers, volunteers, venue staff, elders, and first-time contacts.
- Rules before AI generation: A deterministic helper gives predictable guidance. AI may generate drafts, but runtime educational behavior should be stable and testable. Rule-based guidance is easier to audit in a workshop.
- Separation from UI: The helper does not render HTML. It returns a simple object so the same logic can support web UI, tests, documentation, or future API responses.
Code sample — src/lib/politeness.ts
export type Audience = "peer" | "speaker" | "organizer" | "volunteer" | "venue-staff" | "elder" | "unknown";
export interface PolitenessGuidance {
recommendedVariant: "natural" | "polite";
message: string;
reason: string;
}
const formalAudiences: Audience[] = ["speaker", "organizer", "volunteer", "venue-staff", "elder", "unknown"];
export function getPolitenessGuidance(audience: Audience): PolitenessGuidance {
if (formalAudiences.includes(audience)) {
return {
recommendedVariant: "polite",
message: "Use the polite Tagalog version first.",
reason: "Polite speech is safer with first-time contacts, event staff, speakers, elders, and volunteers."
};
}
return {
recommendedVariant: "natural",
message: "Natural Tagalog is acceptable with peers when the setting feels relaxed.",
reason: "Casual speech can be friendly after rapport is established."
};
}
Code explanation
- Business logic: The helper teaches when respectful speech is safer.
- Code logic: A fixed list of formal audiences controls whether the recommended variant is
politeornatural. - Expected result: Calling
getPolitenessGuidance("speaker")returns a polite recommendation, whilegetPolitenessGuidance("peer")returns natural guidance.
Step 5 — Build a category selector
Kiro prompt sample
Create a CategorySelector React component.
It receives categories, selected category, and onChange callback.
Use accessible buttons and visible active state.
System design decision
- Situation-first navigation: Categories mirror event situations. Developers should avoid forcing learners to scroll through unrelated cards when they need a phrase quickly.
- Controlled component pattern: The selector receives state from its parent. This keeps filtering logic centralized and makes the component simple to test and reuse.
- Buttons over dropdown for workshop demo: Buttons make category switching visible during live instruction. They are easier for participants to inspect, test, and style within the two-hour session.
Code sample — src/components/CategorySelector.tsx
import type { PracticeCategory } from "../data/practiceCards";
interface Props {
categories: PracticeCategory[];
selected: PracticeCategory;
onChange: (category: PracticeCategory) => void;
}
export function CategorySelector({ categories, selected, onChange }: Props) {
return (
<nav aria-label="Practice categories" className="category-selector">
{categories.map((category) => (
<button
key={category}
type="button"
aria-pressed={category === selected}
className={category === selected ? "active" : ""}
onClick={() => onChange(category)}
>
{category}
</button>
))}
</nav>
);
}
Code explanation
- Business logic: Learners choose the event situation they want to practice.
- Code logic: The component maps categories to buttons and reports changes through
onChange. - Expected result: Clicking a category updates the selected practice group in the parent component.
Step 6 — Build prompt/reveal practice cards
Kiro prompt sample
Create a PracticeCardView component.
Show English prompt first.
After the learner clicks Reveal, show natural Tagalog, polite Tagalog, pronunciation, tone, and review status.
If tone is informal-playful, show an Informal badge.
System design decision
- Active recall over passive reading: Prompt/reveal design makes learners think before seeing the answer. This improves practice value compared with always showing all translations immediately.
- Reveal keeps cognitive load low: Beginners can be overwhelmed by grammar, tone, and pronunciation. Showing the English prompt first and revealing details later creates a gentler experience.
- Tone and review remain visible: Even after reveal, learners need to know whether content is draft and whether a style is informal. The UI must not hide quality or tone metadata.
Code sample — src/components/PracticeCardView.tsx
import { useState } from "react";
import type { Audience } from "../lib/politeness";
import { getPolitenessGuidance } from "../lib/politeness";
import type { PracticeCard } from "../data/practiceCards";
interface Props {
card: PracticeCard;
audience: Audience;
}
export function PracticeCardView({ card, audience }: Props) {
const [revealed, setRevealed] = useState(false);
const guidance = getPolitenessGuidance(audience);
return (
<article className="practice-card">
<p className="practice-card__category">{card.category}</p>
<h2>{card.englishPrompt}</h2>
<p>{guidance.message}</p>
<button type="button" onClick={() => setRevealed((value) => !value)}>
{revealed ? "Hide answer" : "Reveal Tagalog"}
</button>
{revealed && (
<section aria-label="Practice answer">
<p><strong>Natural Tagalog:</strong> <span lang="tl">{card.naturalTagalog}</span></p>
<p><strong>Polite Tagalog:</strong> <span lang="tl">{card.politeTagalog}</span></p>
<p><strong>Pronunciation:</strong> {card.pronunciation}</p>
<p><strong>Tone:</strong> {card.tone}</p>
{card.tone === "informal-playful" && <span className="badge">Informal</span>}
<p><strong>Review:</strong> {card.reviewStatus}</p>
<p><strong>Why:</strong> {guidance.reason}</p>
</section>
)}
</article>
);
}
Code explanation
- Business logic: The component creates a learning interaction: read English, choose whether to reveal, then compare natural and polite forms.
- Code logic: React state controls reveal/hide behavior. The politeness helper returns guidance based on audience.
- Expected result: A learner can practice one prompt at a time and receive safer tone guidance for the selected audience.
Step 7 — Compose the educational practice app
Kiro prompt sample
Create App.tsx that lets learners choose category and audience.
Filter practice cards by category and render prompt/reveal cards.
Default category should be greetings and default audience should be unknown.
System design decision
- Filtering before search: For a small workshop app, category filtering is simpler and more explainable than full-text search. It reinforces the event-situation model and keeps the implementation easy to inspect.
- Audience-aware practice: The same sentence may be acceptable in casual or polite contexts. Audience selection helps learners understand why the polite variant is safer with unknown contacts, staff, or speakers.
- Composable state: The app shell owns selected category and audience. Child components remain focused and reusable. This is a clean React design for a workshop because developers can reason about state flow quickly.
Code sample — src/App.tsx
import { useMemo, useState } from "react";
import { practiceCards, type PracticeCategory } from "./data/practiceCards";
import type { Audience } from "./lib/politeness";
import { CategorySelector } from "./components/CategorySelector";
import { PracticeCardView } from "./components/PracticeCardView";
const categories: PracticeCategory[] = ["greetings", "directions", "workshops", "thanks", "waiting"];
const audiences: Audience[] = ["unknown", "peer", "speaker", "organizer", "volunteer", "venue-staff", "elder"];
export default function App() {
const [selectedCategory, setSelectedCategory] = useState<PracticeCategory>("greetings");
const [audience, setAudience] = useState<Audience>("unknown");
const visibleCards = useMemo(
() => practiceCards.filter((card) => card.category === selectedCategory),
[selectedCategory]
);
return (
<main>
<h1>Educational Tagalog Practice for AWS Manila Community Day</h1>
<p>Choose a situation, choose an audience, then reveal the Tagalog answer.</p>
<label>
Audience
<select value={audience} onChange={(event) => setAudience(event.target.value as Audience)}>
{audiences.map((item) => <option key={item} value={item}>{item}</option>)}
</select>
</label>
<CategorySelector categories={categories} selected={selectedCategory} onChange={setSelectedCategory} />
{visibleCards.map((card) => (
<PracticeCardView key={card.id} card={card} audience={audience} />
))}
</main>
);
}
Code explanation
- Business logic: Learners practice phrases by situation and audience.
- Code logic:
useStatetracks category and audience.useMemofilters cards efficiently for the selected category. - Expected result: Selecting “directions” shows direction cards; selecting “speaker” recommends polite Tagalog first.
Step 8 — Add tests and a data-quality hook
Kiro prompt sample
Generate tests for the educational practice flow.
Verify category filtering, reveal behavior, polite guidance for speaker audience, and review status visibility.
Create a Kiro hook that reminds developers to validate reviewStatus when card data changes.
System design decision
- Behavior tests over snapshot tests: The key learning behavior is category filtering and reveal. Tests should simulate learner actions rather than only compare HTML snapshots.
- Politeness guidance must be verified: The app’s differentiator is educational tone support. If an audience selection stops recommending polite speech for speakers, the product fails even if the UI still renders.
- Data change hook: Content updates are frequent in language apps. A Kiro hook that watches
src/data/can remind developers to check review status, informal labels, and pronunciation coverage whenever content changes.
Code sample — src/tests/politeness.test.ts
import { describe, expect, it } from "vitest";
import { getPolitenessGuidance } from "../lib/politeness";
describe("getPolitenessGuidance", () => {
it("recommends polite Tagalog for speakers", () => {
const result = getPolitenessGuidance("speaker");
expect(result.recommendedVariant).toBe("polite");
expect(result.message).toContain("polite Tagalog");
});
it("allows natural Tagalog for relaxed peer context", () => {
const result = getPolitenessGuidance("peer");
expect(result.recommendedVariant).toBe("natural");
});
});
Code explanation
- Business logic: The test confirms the educational rule that polite speech is safer for speakers.
- Code logic: It calls the helper directly and asserts the returned recommendation.
- Expected result: Tests fail if the politeness rules are accidentally weakened.
Kiro hook sample — .kiro/hooks/card-data-quality.md
# Hook: Card data quality reminder
Trigger: when files under `src/data/` are saved.
Action:
1. Check that every card has `reviewStatus`.
2. Check that every card has pronunciation text.
3. Remind the developer that `draft` content requires native-speaker review before production publication.
4. Suggest adding or updating tests if a new category is introduced.
Hook explanation
- Business logic: The hook keeps educational content reviewable as the dataset grows.
- Code logic: It attaches quality reminders to data changes rather than relying on manual memory.
- Expected result: Developers are prompted to maintain review and pronunciation standards whenever content changes.
Completion checklist
- Educational steering exists.
- Kiro spec tasks exist.
- Practice card model includes category, tone, pronunciation, and review status.
- Politeness helper is deterministic and tested.
- Category selector filters cards.
- Prompt/reveal card works.
- Kiro hook guidance exists for data quality.
Optional AWS extension after the workshop
- Host static build on AWS Amplify Hosting.
- Store
practiceCardsas versioned JSON in Amazon S3. - Use Kiro to generate a release checklist for reviewed learning content.
- Add Amazon Bedrock draft-generation only behind a human review workflow.
Additional Hands-on Developer Labs
These labs are unique to Workshop 2 — Educational-First Dev Tips. They extend the core prompt/reveal app with pedagogy, learner confidence, content review, and respectful-language guardrails. The focus is not on generic Kiro setup; it is on turning educational intent into developer-visible product behavior.
Hands-on Lab A — Create a learner-outcome rubric before adding more cards
Developer action
- Ask Kiro to create a rubric that defines what a beginner should be able to do after each category.
- Add the rubric to
.kiro/steering/learning-rubric.md. - Ask Kiro to compare the current
practiceCardsdata against the rubric. - Convert any missing outcomes into small implementation tasks.
Kiro prompt sample
Create a learner-outcome rubric for this Tagalog practice app.
Group outcomes by greetings, directions, workshops, thanks, and waiting.
For each category, define beginner success, respectful-speech expectation, pronunciation expectation, and review-risk notes.
Then inspect the current practice card model and list gaps as implementation tasks.
System design decision
- Rubric before expansion: More cards do not automatically improve learning. The rubric tells developers what each category is supposed to teach before they add data at scale.
- Outcome-driven data review: A card is useful only if it supports a concrete learner action, such as greeting a volunteer politely or asking where a room is.
- Kiro as curriculum reviewer: Kiro can compare implementation artifacts against the rubric and produce actionable gaps, while native speakers still own final language review.
Code sample — .kiro/steering/learning-rubric.md
# Learning Rubric
Each practice category must support one beginner action, one respect marker, and one speaking confidence goal.
## Greetings
- Beginner action: introduce yourself as someone learning Tagalog.
- Respect expectation: show `po` in first-contact greetings.
- Pronunciation expectation: include stress guidance for `Kumusta` and `Tagalog`.
- Review risk: avoid claiming one greeting is correct for every region or setting.
## Directions
- Beginner action: ask where an event room, registration desk, or exit is.
- Respect expectation: use polite question forms with volunteers and venue staff.
- Pronunciation expectation: include short guidance for `Saan po`.
- Review risk: location words should be checked for natural event usage.
## Workshops
- Beginner action: ask permission to ask, repeat, join, or sit.
- Respect expectation: use polite phrasing with speakers and organizers.
- Pronunciation expectation: include gentle-question speaking tips.
- Review risk: avoid wording that sounds demanding.
Code explanation
- Business logic: The rubric defines educational quality before content quantity.
- Code logic: Kiro can use this steering file when generating cards, tests, and review tasks.
- Expected result: Future card additions are checked against learning outcomes instead of being accepted because they compile.
Hands-on Lab B — Add a confidence-level model for beginner practice
Developer action
- Add a learner-facing confidence level to each practice card.
- Ask Kiro to update the TypeScript model and seed data.
- Render confidence level near the prompt so learners know how hard the card is.
- Add a test that verifies every card has a confidence level.
Kiro prompt sample
Add a beginner confidence model to the practice-card data contract.
Use confidenceLevel values: starter, guided, stretch.
Starter cards should be short and safe for first contact.
Guided cards may include a short context note.
Stretch cards may include longer polite phrases.
Update types, sample data, UI label, and tests.
System design decision
- Confidence is part of learning design: Beginners need to know which phrases are safe to try first. A difficulty label prevents the app from treating every sentence as equally approachable.
- Small enum over free text: A fixed union type avoids inconsistent labels such as easy, beginner, simple, or basic.
- Visible metadata: Confidence should be shown in the UI, not hidden in data, because learners use it to choose practice flow.
Code sample — src/data/practiceCards.ts
export type ConfidenceLevel = "starter" | "guided" | "stretch";
export interface PracticeCard {
id: string;
category: PracticeCategory;
englishPrompt: string;
naturalTagalog: string;
politeTagalog: string;
tone: "casual" | "polite" | "event-ready" | "informal-playful";
pronunciation: string;
reviewStatus: ReviewStatus;
confidenceLevel: ConfidenceLevel;
}
export const confidenceLabels: Record<ConfidenceLevel, string> = {
starter: "Start here",
guided: "Practice with context",
stretch: "Try after rehearsal"
};
Code explanation
- Business logic: The model helps learners choose an achievable practice card.
- Code logic:
ConfidenceLevelrestricts valid values, andconfidenceLabelscentralizes display text. - Expected result: New cards cannot omit the intended beginner difficulty without causing a type or validation gap.
Hands-on Lab C — Build a hint ladder for prompt/reveal practice
Developer action
- Ask Kiro to add optional hints that appear before the full Tagalog answer.
- Implement a helper that returns ordered hints for a card.
- Update the practice card UI with “Show hint” and “Reveal answer” actions.
- Add tests that verify hints appear before the answer.
Kiro prompt sample
Create a hint ladder for prompt/reveal practice.
Hints should reveal category context, first word, respect marker, and pronunciation cue before showing the full answer.
Keep hints deterministic and derived from the existing card object.
Add component tests for the hint-first learning flow.
System design decision
- Scaffold recall instead of giving answers immediately: A hint ladder encourages active memory before full reveal.
- Derived hints reduce content burden: The first version can derive hints from existing card fields instead of asking authors to maintain a new hint dataset.
- Deterministic behavior is testable: The UI can prove that hints appear in sequence and that full answers stay hidden until requested.
Code sample — src/lib/hints.ts
import type { PracticeCard } from "../data/practiceCards";
export interface PracticeHint {
label: string;
value: string;
}
export function buildHintLadder(card: PracticeCard): PracticeHint[] {
const firstWord = card.politeTagalog.split(/\s+/)[0] ?? "";
const hasPo = /\bpo\b/i.test(card.politeTagalog);
return [
{ label: "Situation", value: `This is for ${card.category}.` },
{ label: "First word", value: firstWord },
{
label: "Respect marker",
value: hasPo ? "Listen for po in the polite version." : "No po marker appears in this phrase."
},
{ label: "Pronunciation cue", value: card.pronunciation }
];
}
Code explanation
- Business logic: Hints help learners rehearse without immediately copying the answer.
- Code logic: The helper reads
category,politeTagalog, andpronunciationto create predictable hint steps. - Expected result: A learner can request guidance gradually before revealing the complete phrase.
Hands-on Lab D — Add an audience-safety matrix for polite guidance
Developer action
- Ask Kiro to create a matrix of audiences and recommended tone behavior.
- Replace single-message politeness guidance with structured policy output.
- Add unit tests for every audience value.
- Ask Kiro to identify any UI labels that could confuse learners.
Kiro prompt sample
Refactor the politeness helper into an audience-safety matrix.
For each audience, return recommendedVariant, safetyLevel, reason, and learnerWarning.
Use stricter guidance for speakers, organizers, volunteers, venue staff, elders, and unknown contacts.
Generate Vitest cases for every audience value.
System design decision
- Policy table over nested conditionals: A matrix is easier to review with educators and native speakers than scattered if statements.
- Safety level supports UI decisions: The app can show stronger warnings for unknown contacts or blocked content without changing the card schema.
- Exhaustive tests protect tone rules: Each audience value should have a test so a future refactor cannot silently weaken respectful-speech guidance.
Code sample — src/lib/audiencePolicy.ts
export type Audience = "peer" | "speaker" | "organizer" | "volunteer" | "venue-staff" | "elder" | "unknown";
export type SafetyLevel = "relaxed" | "careful" | "strict";
export interface AudiencePolicy {
recommendedVariant: "natural" | "polite";
safetyLevel: SafetyLevel;
reason: string;
learnerWarning: string;
}
export const audiencePolicy: Record<Audience, AudiencePolicy> = {
peer: {
recommendedVariant: "natural",
safetyLevel: "relaxed",
reason: "Natural Tagalog can be friendly with peers after rapport is established.",
learnerWarning: "Use polite wording if you are unsure."
},
speaker: {
recommendedVariant: "polite",
safetyLevel: "strict",
reason: "Speakers are leading the session, so respectful speech is safer.",
learnerWarning: "Start with the polite version."
},
organizer: {
recommendedVariant: "polite",
safetyLevel: "strict",
reason: "Organizers manage the event and may be first-time contacts.",
learnerWarning: "Use polite wording first."
},
volunteer: {
recommendedVariant: "polite",
safetyLevel: "careful",
reason: "Volunteers are helping attendees, so respectful questions are appropriate.",
learnerWarning: "Use po when asking for help."
},
"venue-staff": {
recommendedVariant: "polite",
safetyLevel: "strict",
reason: "Venue staff are professional contacts in a public setting.",
learnerWarning: "Use the polite version first."
},
elder: {
recommendedVariant: "polite",
safetyLevel: "strict",
reason: "Respectful speech is safer with elders.",
learnerWarning: "Use polite markers unless advised otherwise."
},
unknown: {
recommendedVariant: "polite",
safetyLevel: "strict",
reason: "Unknown contacts require the safest default.",
learnerWarning: "Choose polite Tagalog until the setting is clearly casual."
}
};
Code explanation
- Business logic: The policy teaches safer audience-aware speech choices.
- Code logic: A
Record<Audience, AudiencePolicy>makes missing audience entries visible to TypeScript. - Expected result: UI and tests can consume one clear policy object for all tone guidance.
Hands-on Lab E — Create a content review queue for native-speaker feedback
Developer action
- Ask Kiro to generate a review queue from draft cards.
- Add a function that groups cards by category and review status.
- Render a developer-only review summary in the app or a console report.
- Add a test that blocked cards never appear in the learner’s default practice list.
Kiro prompt sample
Create a content review queue for the Tagalog practice cards.
Group cards by reviewStatus and category.
Draft cards should show reviewer questions.
Blocked cards should be excluded from default learner practice.
Add a small testable helper and a developer-facing summary component.
System design decision
- Review workflow is a feature: Since language content requires native-speaker review, developers need a way to see what remains unreviewed.
- Separate learner view from reviewer view: Learners should not receive blocked content as recommended practice, but reviewers still need to inspect it.
- Group by category: Reviewers can evaluate phrases in context instead of scanning a flat list.
Code sample — src/lib/reviewQueue.ts
import type { PracticeCard, PracticeCategory, ReviewStatus } from "../data/practiceCards";
export type ReviewQueue = Record<ReviewStatus, Partial<Record<PracticeCategory, PracticeCard[]>>>;
export function buildReviewQueue(cards: PracticeCard[]): ReviewQueue {
return cards.reduce<ReviewQueue>((queue, card) => {
queue[card.reviewStatus] ??= {};
queue[card.reviewStatus][card.category] ??= [];
queue[card.reviewStatus][card.category]!.push(card);
return queue;
}, { draft: {}, "native-reviewed": {}, blocked: {} });
}
export function learnerPracticeCards(cards: PracticeCard[]) {
return cards.filter((card) => card.reviewStatus !== "blocked");
}
Code explanation
- Business logic: The helper separates review management from learner practice.
- Code logic:
buildReviewQueuegroups cards by status and category, whilelearnerPracticeCardsfilters unsafe content. - Expected result: Developers can see what needs review, and blocked content is not promoted to learners.
Hands-on Lab F — Add an instructor exit-ticket and learning analytics event model
Developer action
- Ask Kiro to define local-only analytics events for workshop learning outcomes.
- Add event types for category selected, hint viewed, answer revealed, and confidence selected.
- Keep events in memory or console for the workshop; do not send telemetry externally.
- Generate an instructor exit-ticket template based on the event names.
Kiro prompt sample
Define a local-only learning event model for the workshop app.
Track category_selected, hint_viewed, answer_revealed, and confidence_marked.
Do not add external telemetry or network calls.
Create a short instructor exit-ticket template that asks learners what they practiced and where they still need help.
System design decision
- Learning analytics without external complexity: A local event model helps instructors discuss behavior while keeping the workshop privacy-friendly and setup-free.
- Events map to pedagogy: The events correspond to learning actions, not marketing metrics.
- Exit ticket closes the loop: Developers see how product instrumentation can support teaching improvement.
Code sample — src/lib/learningEvents.ts
export type LearningEventName =
| "category_selected"
| "hint_viewed"
| "answer_revealed"
| "confidence_marked";
export interface LearningEvent {
name: LearningEventName;
cardId?: string;
category?: string;
value?: string;
occurredAt: string;
}
const events: LearningEvent[] = [];
export function recordLearningEvent(event: Omit<LearningEvent, "occurredAt">) {
events.push({ ...event, occurredAt: new Date().toISOString() });
}
export function readLearningEvents() {
return [...events];
}
Code explanation
- Business logic: The model captures learner actions that can inform future teaching design.
- Code logic: Events are stored in memory for the workshop and can be inspected without adding a service.
- Expected result: Participants can discuss how educational products measure practice behavior responsibly.
Reference architecture notes
- Kiro capabilities emphasized in this workshop: pedagogy steering, learner-outcome review, controlled TypeScript refactoring, rule-based politeness policy, tests from educational acceptance criteria, and review-aware development.
- Product scope: beginner-friendly Tagalog practice for event preparation. Language content remains draft until reviewed by native Tagalog speakers.
- Runtime scope: local React app first. Optional deployment can use AWS Amplify Hosting or Amazon S3 after review workflows are established.
