AWS Builder Workshop

Build with Kiro: Prompt-First Product Design for a Tagalog Learning App Workshop

Audience: professional developers familiar with TypeScript, React, JSON, and basic testing
Duration: 2 hours
Primary AWS AI service: Kiro
Project output: a static React-based sentence-card app that turns a product prompt into a reviewed schema, UI, tests, and release checklist.

Workshop Summary

Tagalog Kiro Workshop

This workshop guides developers from a raw product prompt to a tested Tagalog sentence-card application. Participants use Kiro to define product intent, shape requirements, design a typed card schema, build a React renderer, and add validation. By the end, they understand how prompt-first planning turns language-learning ideas into reviewable implementation tasks and reliable release checks for safer, consistent learner experiences.

What developers will build

Developers will build a Tagalog sentence-card learning app for AWS Manila Community Day. The app stores curated sentence cards and renders each card with natural Tagalog, polite Tagalog, friendly Filipino-English, playful Filipino-English, tone, cultural context, grammar, examples, and pronunciation. The workshop uses Kiro as the AI engineering environment: developers create steering docs, generate specs, review requirements, convert specs into implementation tasks, create code, generate tests, and automate quality checks with hooks.

2-hour agenda

Time Module Developer outcome
0–10 min Setup Kiro workspace Project opened with steering docs generated
10–25 min Product contract Sentence-card schema specified before UI
25–45 min Kiro specs Requirements, design, and tasks created
45–70 min Data model and sample cards Typed content contract implemented
70–95 min React renderer Sentence cards rendered with mobile-first UX
95–110 min Tests and hooks Schema tests and Kiro hook guidance added
110–120 min Review and extension Developers know how to extend categories safely

Prerequisites

node --version   # 20+
npm --version

Recommended local structure:

tagalog-prompt-first-app/
  .kiro/
    steering/
    specs/tagalog-learning-app/
  src/
    data/
    components/
    lib/
    tests/
  package.json

Step 1 — Create the Kiro workspace and steering files

Kiro prompt sample

Create foundational steering docs for a Tagalog learning app for AWS Manila Community Day.
The app is educational, non-commercial, and beginner-friendly.
Use TypeScript, React, Vite, Vitest, and CSS modules.
The app must separate natural Tagalog, polite Tagalog, friendly Filipino-English, and playful Filipino-English.
Always label playful style as informal.
Language content requires native-speaker review before production use.

System design decision

  1. Persistent context before generation: Steering files are used first because the app has domain rules that should not be repeated in every Kiro chat. The domain is not only UI rendering; it includes cultural respect, politeness markers, playful style boundaries, and review requirements. Encoding these rules in .kiro/steering/product.md, .kiro/steering/tech.md, and .kiro/steering/structure.md gives Kiro durable context so later code suggestions follow the same product expectations.
  2. Workspace-local governance: Keep steering files in the repository instead of relying only on individual memory. Professional teams need reviewable AI guidance the same way they review architecture decision records. Local steering makes product scope, technology choices, and file conventions visible in pull requests, onboarding, audits, and future workshops.
  3. Human-review safety: The app teaches language and culture, so generated content cannot be treated as authoritative. The steering layer explicitly requires native-speaker review, beginner-safe wording, and avoidance of stereotypes. This design makes quality assurance a system-level concern rather than a final manual reminder.

Code sample — .kiro/steering/product.md

# Product Overview

This workspace builds an educational Tagalog learning app for AWS Manila Community Day preparation.
The app helps beginners practice polite event phrases for greetings, directions, workshops, volunteers, waiting time, and goodbye.

## Product rules

- Show natural Tagalog before playful Filipino-English.
- Show polite Tagalog when speaking to speakers, organizers, volunteers, venue staff, elders, or first-time contacts.
- Explain `po`, `opo`, `kayo`, and `ninyo` as respect markers when appropriate.
- Label playful Filipino-English as informal.
- Keep examples short enough for mobile reading.
- Require native-speaker review before production publication.

Code explanation

  • Business logic: This file defines the learning product rules that affect every generated feature. It prevents the app from becoming a generic translator and keeps the workshop focused on event-ready learning.
  • Code logic: Although this is Markdown, Kiro treats it as persistent project context. Future generated code, tests, and docs should respect these rules.
  • Expected result: When developers ask Kiro to build components or tests, Kiro should keep natural, polite, friendly, and playful outputs separated and should include review reminders.

Step 2 — Turn the product idea into a Kiro spec

Kiro prompt sample

Create a spec named tagalog-learning-app.
Feature: render reviewed sentence cards for AWS Manila Community Day preparation.
Generate requirements, design, and implementation tasks.
Use EARS-style acceptance criteria.
The first version is static and uses local JSON data only.

System design decision

  1. Spec-driven development over direct coding: The workshop starts from a spec because the product requires traceability between user goals, data fields, UI behavior, and tests. A direct code-first approach may build screens quickly but makes it hard to verify whether politeness, tone labels, grammar support, and mobile readability are consistently handled. The spec becomes the source of truth for implementation.
  2. Static first, cloud-ready later: The first version uses local JSON because the core risk is content shape, not infrastructure. A static app lets developers validate schema, rendering, testing, and review flow inside two hours. After the contract is proven, the same data shape can move to S3, DynamoDB, or an API without redesigning the learning card.
  3. Acceptance criteria as tests: EARS-style statements make product behavior testable. For example, “WHEN a card has playful Filipino-English, THEN the UI SHALL show an informal label.” This turns cultural and educational requirements into implementation constraints, which is essential for professional developer workshops.

Code sample — .kiro/specs/tagalog-learning-app/requirements.md

# Requirements

## Requirement 1: Sentence card rendering

User story: As a beginner learner, I want to see one English sentence with natural, polite, friendly, and playful variants so that I can choose the right tone at the event.

Acceptance criteria:

1. WHEN a sentence card is rendered THEN the system SHALL show English input, natural Tagalog, polite Tagalog, friendly Filipino-English, playful Filipino-English, and tone.
2. WHEN playful Filipino-English is shown THEN the system SHALL label it as informal.
3. WHEN polite Tagalog is shown THEN the system SHALL include cultural context for when to use respectful speech.

## Requirement 2: Mobile-first learning

Acceptance criteria:

1. WHEN the viewport is narrow THEN the sentence card SHALL remain readable without horizontal scrolling.
2. WHEN grammar notes are rendered THEN the system SHALL use short list items.
3. WHEN pronunciation is rendered THEN it SHALL appear near the Tagalog phrase.

Code explanation

  • Business logic: The requirements describe the learner experience and define what the app must teach.
  • Code logic: Each acceptance criterion can map to a component property, DOM assertion, or accessibility check.
  • Expected result: Kiro can generate tasks and tests that verify the product behavior instead of only checking that components compile.

Step 3 — Define the sentence-card data contract

Kiro prompt sample

Generate a TypeScript data contract for a Tagalog sentence card.
Include fields for English input, natural Tagalog, polite Tagalog, friendly Filipino-English, playful Filipino-English, tone, cultural context, grammar breakdown, examples, pronunciation, category, and review status.
Add two sample cards from the Community Day scenario.

System design decision

  1. Schema before content scale: A stable TypeScript type protects the app from inconsistent AI-generated content. Once the contract is defined, every sentence card must include the same required fields. This prevents a common AI-assisted development problem where each generated card has slightly different labels, missing grammar notes, or inconsistent example formats.
  2. Review status as first-class data: reviewStatus is included in the schema because language content has quality risk. Developers often put review notes in comments or spreadsheets, but the application needs to know whether content is draft, reviewed, or blocked. Treating review state as data enables filters, badges, release gates, and future moderation workflows.
  3. Multiple tone variants as separate fields: Natural Tagalog, polite Tagalog, friendly Filipino-English, and playful Filipino-English are separate fields because they serve different learning purposes. Mixing them into one text blob would make UI rendering, testing, and learner comprehension weaker. Field separation also allows future search, tagging, and analytics.

Code sample — src/data/cards.ts

export type ReviewStatus = "draft" | "native-reviewed" | "blocked";

export interface GrammarItem {
  term: string;
  meaning: string;
}

export interface ExampleSentence {
  tagalog: string;
  english: string;
}

export interface PronunciationGuide {
  full: string;
  stress: string;
  speakingTip: string;
}

export interface SentenceCard {
  id: string;
  category: "greetings" | "directions" | "workshops" | "volunteers" | "waiting";
  englishInput: string;
  naturalTagalog: string;
  politeTagalog: string;
  friendlyFilipinoEnglish: string;
  playfulFilipinoEnglish: string;
  tone: string;
  culturalContext: string;
  grammar: GrammarItem[];
  examples: ExampleSentence[];
  pronunciation: PronunciationGuide;
  reviewStatus: ReviewStatus;
}

export const cards: SentenceCard[] = [
  {
    id: "greeting-001",
    category: "greetings",
    englishInput: "Hello, I am learning Tagalog.",
    naturalTagalog: "Kumusta, nag-aaral ako ng Tagalog.",
    politeTagalog: "Kumusta po, nag-aaral po ako ng Tagalog.",
    friendlyFilipinoEnglish: "Hello po, learning Tagalog ako.",
    playfulFilipinoEnglish: "Kumusta, learning Tagalog na ako, all right.",
    tone: "friendly, beginner-friendly, event-ready",
    culturalContext:
      "Use the polite version with volunteers, speakers, organizers, venue staff, elders, or people you meet for the first time.",
    grammar: [
      { term: "Kumusta", meaning: "hello or how are you" },
      { term: "po", meaning: "politeness marker" },
      { term: "nag-aaral", meaning: "studying or learning" }
    ],
    examples: [
      { tagalog: "Kumusta po kayo?", english: "How are you?" },
      { tagalog: "Nag-aaral po ako.", english: "I am learning." },
      { tagalog: "Salamat po sa tulong.", english: "Thank you for the help." }
    ],
    pronunciation: {
      full: "koo-MOOS-tah poh, nag-ah-AH-ral poh AH-koh ngah tah-GAH-log",
      stress: "Stress MOOS, AH, and GAH.",
      speakingTip: "Say po softly and keep the greeting warm."
    },
    reviewStatus: "draft"
  },
  {
    id: "workshop-001",
    category: "workshops",
    englishInput: "May I ask a question?",
    naturalTagalog: "Puwede ba akong magtanong?",
    politeTagalog: "Puwede po ba akong magtanong?",
    friendlyFilipinoEnglish: "Can I ask po?",
    playfulFilipinoEnglish: "Question time na ako, all right?",
    tone: "polite, practical, workshop-ready",
    culturalContext:
      "Use the polite version before asking speakers, mentors, or organizers a question during a session.",
    grammar: [
      { term: "Puwede", meaning: "may or can" },
      { term: "ba", meaning: "question marker" },
      { term: "magtanong", meaning: "to ask" }
    ],
    examples: [
      { tagalog: "Puwede po ba akong umupo dito?", english: "May I sit here?" },
      { tagalog: "Puwede po bang pakiulit?", english: "Could you please repeat?" },
      { tagalog: "Puwede po bang sumali?", english: "May I join?" }
    ],
    pronunciation: {
      full: "PWEH-deh poh bah AH-kong mag-tah-NONG",
      stress: "Stress PWEH, AH, and NONG.",
      speakingTip: "Make the question sound gentle, not demanding."
    },
    reviewStatus: "draft"
  }
];

Code explanation

  • Business logic: The schema represents the learning promise of the app: one sentence, multiple tones, grammar, examples, pronunciation, and review status.
  • Code logic: TypeScript interfaces enforce the expected shape. The cards array gives the renderer strongly typed data.
  • Expected result: The app can render every sentence card consistently. Type errors appear if a developer forgets a required field.

Step 4 — Build the React sentence-card renderer

Kiro prompt sample

Create a React component named SentenceCardView.
It accepts a SentenceCard and renders every field with accessible headings and lists.
Playful Filipino-English must show an Informal badge.
Review status must be visible.

System design decision

  1. Component-per-contract: The renderer receives one SentenceCard and does not fetch data directly. This separation makes the UI predictable, testable, and reusable. Developers can later load cards from local JSON, an API, or static generated files without changing rendering logic.
  2. Explicit tone labeling: The UI displays playful Filipino-English with an informal badge because learners may copy what they see. Tone labels are not decorative; they are safety and education controls. The component should teach that playful language is not the same as formal Tagalog.
  3. Accessible content hierarchy: Language cards can become dense. Semantic sections, headings, lists, and readable labels make the app usable on mobile and friendly to assistive technologies. Accessibility is part of the system design because the target environment is a public community event with varied devices.

Code sample — src/components/SentenceCardView.tsx

import type { SentenceCard } from "../data/cards";
import "./SentenceCardView.css";

interface Props {
  card: SentenceCard;
}

export function SentenceCardView({ card }: Props) {
  return (
    <article className="sentence-card" aria-labelledby={`${card.id}-title`}>
      <header className="sentence-card__header">
        <p className="sentence-card__category">{card.category}</p>
        <h2 id={`${card.id}-title`}>{card.englishInput}</h2>
        <span className={`review review--${card.reviewStatus}`}>{card.reviewStatus}</span>
      </header>

      <section aria-label="Tagalog variants" className="sentence-card__variants">
        <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>Friendly Filipino-English:</strong> {card.friendlyFilipinoEnglish}</p>
        <p>
          <strong>Playful Filipino-English:</strong> {card.playfulFilipinoEnglish}
          <span className="badge">Informal</span>
        </p>
      </section>

      <section aria-label="Learning notes">
        <p><strong>Tone:</strong> {card.tone}</p>
        <p><strong>Cultural context:</strong> {card.culturalContext}</p>
      </section>

      <section aria-labelledby={`${card.id}-grammar`}>
        <h3 id={`${card.id}-grammar`}>Grammar breakdown</h3>
        <ul>
          {card.grammar.map((item) => (
            <li key={item.term}><strong>{item.term}:</strong> {item.meaning}</li>
          ))}
        </ul>
      </section>

      <section aria-labelledby={`${card.id}-examples`}>
        <h3 id={`${card.id}-examples`}>Examples</h3>
        <ol>
          {card.examples.map((example) => (
            <li key={example.tagalog}>
              <span lang="tl">{example.tagalog}</span><br />
              <span>{example.english}</span>
            </li>
          ))}
        </ol>
      </section>

      <section aria-labelledby={`${card.id}-pronunciation`}>
        <h3 id={`${card.id}-pronunciation`}>Pronunciation</h3>
        <p>{card.pronunciation.full}</p>
        <p>{card.pronunciation.stress}</p>
        <p>{card.pronunciation.speakingTip}</p>
      </section>
    </article>
  );
}

Code explanation

  • Business logic: The component transforms a reviewed learning card into a learner-facing lesson.
  • Code logic: It renders structured sections from the typed card, maps grammar and examples to lists, adds lang="tl" for Tagalog text, and applies an informal badge to playful output.
  • Expected result: Developers see a complete card with all required learning fields, and learners can identify which sentence is natural, polite, friendly, or informal.

Step 5 — Add responsive styling

Kiro prompt sample

Create mobile-first CSS for SentenceCardView.
Use readable spacing, high contrast, a visible informal badge, and no horizontal scrolling on small screens.

System design decision

  1. Mobile-first because the use case is event preparation: Learners may use the app while traveling, waiting in line, or sitting in a session. The interface must prioritize readability on small screens, not only desktop presentation.
  2. Cards over tables: A table layout would compress long language fields and hurt readability. Cards allow each learning section to breathe, especially grammar and pronunciation. This is a product decision because language learning needs scanning and speaking practice.
  3. Visual state for review and tone: Badges and review labels help users understand confidence level and tone quickly. In a learning product, visual hierarchy reduces cognitive load and prevents informal content from being mistaken as the recommended formal answer.

Code sample — src/components/SentenceCardView.css

.sentence-card {
  border: 1px solid #d0d7de;
  border-radius: 16px;
  padding: 1rem;
  margin: 1rem 0;
  background: #ffffff;
  color: #1f2328;
  line-height: 1.6;
}

.sentence-card__header {
  display: grid;
  gap: 0.5rem;
}

.sentence-card__category {
  margin: 0;
  font-size: 0.85rem;
  color: #57606a;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.sentence-card__variants {
  border-left: 4px solid #ff9900;
  padding-left: 1rem;
}

.badge,
.review {
  display: inline-block;
  margin-left: 0.5rem;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 700;
}

.badge {
  background: #fff3cd;
  color: #7a4d00;
}

.review--draft {
  background: #eaeef2;
  color: #57606a;
}

.review--native-reviewed {
  background: #dafbe1;
  color: #116329;
}

.review--blocked {
  background: #ffebe9;
  color: #82071e;
}

@media (min-width: 760px) {
  .sentence-card {
    padding: 1.5rem;
  }

  .sentence-card__header {
    grid-template-columns: 1fr auto;
    align-items: start;
  }
}

Code explanation

  • Business logic: Styling supports the learning task by making categories, tone, and review status easy to scan.
  • Code logic: The CSS uses a card container, responsive grid header, left accent for variants, and badge styles for informal/review states.
  • Expected result: The UI remains readable on mobile and becomes more spacious on larger screens.

Step 6 — Render the app shell

Kiro prompt sample

Create App.tsx that renders all sentence cards from src/data/cards.ts.
Add a short disclaimer that generated content requires native-speaker review.

System design decision

  1. Static shell for workshop reliability: In a two-hour workshop, the app should run without external APIs or network credentials. This reduces setup friction and lets developers focus on Kiro’s spec-to-code workflow.
  2. Disclaimer in product UI: A language-learning prototype must communicate review status to users. Placing the disclaimer in the app shell makes the limitation visible during demos instead of hidden in documentation.
  3. Data import instead of hardcoded JSX: The shell maps over cards, which proves that the UI is data-driven. When more content is generated or reviewed, the app scales by adding data rather than duplicating UI markup.

Code sample — src/App.tsx

import { cards } from "./data/cards";
import { SentenceCardView } from "./components/SentenceCardView";

export default function App() {
  return (
    <main className="app-shell">
      <header>
        <h1>Tagalog Learning Cards for AWS Manila Community Day</h1>
        <p>
          Educational prototype. Review Tagalog translations, grammar notes, and cultural guidance
          with native speakers before production use.
        </p>
      </header>

      {cards.map((card) => (
        <SentenceCardView key={card.id} card={card} />
      ))}
    </main>
  );
}

Code explanation

  • Business logic: The shell presents the app as an educational prototype and renders all available learning cards.
  • Code logic: React maps the typed cards array to reusable SentenceCardView components.
  • Expected result: Running the app shows a heading, review disclaimer, and one UI card per data item.

Step 7 — Generate tests and use a Kiro hook for quality checks

Kiro prompt sample

Generate Vitest tests for SentenceCardView.
Verify that natural Tagalog, polite Tagalog, informal badge, grammar notes, examples, pronunciation, and review status render correctly.
Then create a Kiro hook idea that runs tests when source files are saved.

System design decision

  1. Tests mirror acceptance criteria: The tests should not only check snapshots. They should assert that required learning fields appear and that playful content is labeled informal. This keeps implementation aligned with the spec and prevents regressions when more cards or styles are added.
  2. Hook-assisted discipline: Kiro hooks are valuable because developers frequently forget repetitive validation steps during fast AI-assisted builds. A file-save hook or pre-commit hook can remind the team to run tests, update docs, or validate card schema.
  3. Quality gates for generated content: AI-assisted code and content should pass deterministic checks. Tests provide a stable safety net while human review handles linguistic nuance. This layered quality design separates mechanical correctness from cultural correctness.

Code sample — src/tests/SentenceCardView.test.tsx

import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { SentenceCardView } from "../components/SentenceCardView";
import { cards } from "../data/cards";

describe("SentenceCardView", () => {
  it("renders all required learning sections", () => {
    render(<SentenceCardView card={cards[0]} />);

    expect(screen.getByText(cards[0].englishInput)).toBeInTheDocument();
    expect(screen.getByText(cards[0].naturalTagalog)).toBeInTheDocument();
    expect(screen.getByText(cards[0].politeTagalog)).toBeInTheDocument();
    expect(screen.getByText(cards[0].friendlyFilipinoEnglish)).toBeInTheDocument();
    expect(screen.getByText(cards[0].playfulFilipinoEnglish)).toBeInTheDocument();
    expect(screen.getByText("Informal")).toBeInTheDocument();
    expect(screen.getByText("Grammar breakdown")).toBeInTheDocument();
    expect(screen.getByText("Examples")).toBeInTheDocument();
    expect(screen.getByText("Pronunciation")).toBeInTheDocument();
    expect(screen.getByText(cards[0].reviewStatus)).toBeInTheDocument();
  });
});

Code explanation

  • Business logic: The test confirms that every sentence card teaches the required learning dimensions.
  • Code logic: Testing Library renders the component and searches for expected text from the first card.
  • Expected result: The test passes when the UI renders all required card fields and fails if a field disappears.

Kiro hook sample — .kiro/hooks/run-tests-on-save.md

# Hook: Run tests on source save

Trigger: when files under `src/` are saved.
Action:
1. Run `npm test -- --run`.
2. If tests fail, summarize the failing test and suggest the smallest fix.
3. If card data changed, remind the developer to verify reviewStatus and native-speaker review notes.

Hook explanation

  • Business logic: The hook protects the educational contract during rapid iteration.
  • Code logic: It links source-file changes to test execution and review reminders.
  • Expected result: Developers receive immediate feedback when UI or data changes break learning requirements.

Step 8 — Final Kiro review and extension tasks

Kiro prompt sample

Review the implementation against the spec.
Find missing requirements, weak tests, unclear labels, and mobile-readability risks.
Suggest the next five tasks to add directions, volunteer thanks, waiting time, and goodbye categories.

System design decision

  1. Review before scale: The app should not generate dozens of categories before the first two cards are correct. A professional workflow validates the contract, component, tests, and review process before expanding content.
  2. Kiro as reviewer, not only generator: Kiro can explain gaps between specs and code, propose next tasks, and generate documentation. This helps developers use AI as an engineering collaborator rather than a code-completion shortcut.
  3. Incremental category growth: New categories should reuse the same schema and component. This keeps the architecture simple and prevents feature drift. The product scales through data and tests, not through new one-off pages.

Completion checklist

  • .kiro/steering/product.md documents product rules.
  • .kiro/specs/tagalog-learning-app/requirements.md exists.
  • TypeScript SentenceCard contract exists.
  • At least two cards render correctly.
  • Playful Filipino-English shows an informal badge.
  • Review status appears in UI.
  • Component tests pass.
  • Kiro hook guidance exists for test automation.

Optional AWS extension after the workshop

  • Host the static build on AWS Amplify Hosting or Amazon S3.
  • Add Amazon CloudFront for global delivery.
  • Store reviewed cards in Amazon S3 JSON.
  • Use Amazon Bedrock later for controlled draft-card generation, with human review before publication.

Additional Hands-on Developer Labs

The following labs deepen the workshop with more concrete developer actions. They are designed for professional developers who want to practice Kiro as an engineering workflow tool, not only as a chat assistant. The labs intentionally use Kiro steering, specs, agentic chat, hooks, task execution, test generation, documentation generation, review support, and MCP-ready integration patterns.

Hands-on Lab A — Bootstrap the repository with Kiro agentic chat

Developer action

  1. Open the project folder in Kiro.
  2. Ask Kiro to inspect the empty workspace and propose a minimal structure.
  3. Ask Kiro to generate the initial files, then review the diff before accepting changes.
  4. Run the local app or generator command manually after Kiro creates the files.

Kiro prompt sample

Inspect this workspace and create a minimal implementation plan.
Use the existing steering and spec files if present.
Do not generate unnecessary infrastructure.
Create only the files needed for the first vertical slice.
After proposing changes, explain each file and why it exists.

System design decision

  1. Start with workspace inspection: Kiro should first understand what exists before generating files. This prevents duplicate folders, conflicting dependencies, and accidental redesigns. In a professional workshop, this mirrors how a developer joins an existing codebase: inspect, understand, then change. Workspace inspection also lets Kiro connect steering files, specs, and source code before implementation.
  2. Generate a vertical slice: The first implementation should prove the smallest complete path from data to visible result. A vertical slice is better than building many disconnected utilities because it validates the product contract, renderer, test setup, and developer workflow together. This gives participants fast feedback and prevents overengineering.
  3. Review diffs before accepting: Kiro can generate useful code quickly, but professional developers still own the codebase. Reviewing diffs reinforces accountability, catches unwanted assumptions, and teaches participants to collaborate with Kiro as an engineering assistant rather than delegating blindly.

Code sample — package.json for React workshop variants

{
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "test": "vitest --environment jsdom",
    "test:run": "vitest --environment jsdom --run",
    "lint:data": "node scripts/validate-cards.mjs"
  },
  "dependencies": {
    "@vitejs/plugin-react": "latest",
    "vite": "latest",
    "typescript": "latest",
    "react": "latest",
    "react-dom": "latest"
  },
  "devDependencies": {
    "vitest": "latest",
    "@testing-library/react": "latest",
    "@testing-library/jest-dom": "latest",
    "jsdom": "latest"
  }
}

Code explanation

  • Business logic: The scripts define the developer feedback loop: run the app, build production output, execute tests, and validate learning-card data.
  • Code logic: dev starts Vite, build compiles TypeScript and bundles the app, test runs component tests, and lint:data runs a custom card validator.
  • Expected result: Developers can use the same command vocabulary throughout the workshop and Kiro can reference these scripts in generated hooks and task plans.

Hands-on Lab B — Generate and refine Kiro steering files

Developer action

  1. Generate foundational steering files in Kiro.
  2. Edit the product, tech, and structure steering files manually.
  3. Ask Kiro to summarize how the steering files will affect future implementation.
  4. Create one additional steering file for language-content review.

Kiro prompt sample

Generate foundational steering docs for this workspace.
Then add a language-review steering file.
The app teaches Tagalog for event preparation, so the implementation must keep review status visible, label informal content, and avoid presenting generated language as final.
Explain how each steering file will guide future code generation.

System design decision

  1. Separate product, tech, and content rules: Product rules explain the learner outcome, tech rules constrain implementation choices, and content rules protect educational quality. Splitting these concerns makes steering easier to review and update. It also helps Kiro retrieve the right context when generating a component, test, hook, or documentation page.
  2. Make review policy persistent: The native-speaker review requirement must survive beyond one chat message. If it is only written in a temporary conversation, future generated cards may omit review status or overstate correctness. A persistent steering file makes the policy part of the workspace’s operating model.
  3. Use steering as team alignment: In a developer workshop, participants may produce variations of the project. Steering files make those variations converge around the same standards. This is especially useful when multiple developers ask Kiro for code in parallel and still need consistent output.

Code sample — .kiro/steering/language-review.md

# Language Review Standards

All Tagalog learning content is educational draft content until reviewed by a native speaker.
The UI must show review status where learners can see it.
Do not hide review state in comments, logs, or documentation only.

## Required fields

- `reviewStatus`: draft, native-reviewed, or blocked
- `reviewNotes`: short reviewer-facing note
- `lastReviewedAt`: ISO date string or null
- `reviewedBy`: reviewer name or null

## Generation rules

- Natural Tagalog appears before playful Filipino-English.
- Playful Filipino-English must be labeled informal.
- Polite Tagalog must explain when respectful speech is safer.
- Do not claim final linguistic authority without review.

Code explanation

  • Business logic: The file defines governance for language quality and learner transparency.
  • Code logic: Kiro can use this Markdown as durable workspace context when generating types, UI labels, validation scripts, and tests.
  • Expected result: Future generated features include visible review metadata and do not treat draft content as final.

Hands-on Lab C — Use Kiro specs to create requirements, design, and task tracking

Developer action

  1. Ask Kiro to create a spec for the next feature.
  2. Review requirements before accepting the design.
  3. Ask Kiro to convert accepted design into implementation tasks.
  4. Execute tasks one at a time, reviewing diffs after each task.

Kiro prompt sample

Create a spec for a review-aware sentence-card feature.
Requirements must include visible review status, informal label for playful Filipino-English, pronunciation display, grammar notes, and mobile readability.
After requirements, create design and sequenced implementation tasks.
Do not implement until the tasks are reviewed.

System design decision

  1. Requirements before implementation: Specs force the team to agree on behavior before code exists. This is valuable because AI-assisted implementation can otherwise create plausible but unreviewed behavior. Requirements define what the product must do and what tests should later verify.
  2. Design before tasks: A design section explains component boundaries, data flow, validation points, and accessibility expectations. Without design, tasks may become a checklist of files rather than an architecture. Kiro is more effective when it has design intent, not only feature requests.
  3. Task-by-task execution: Implementing one task at a time keeps diffs small and teaches developers to review AI output. It also makes failures easier to isolate. If the component test fails after one task, the cause is much clearer than after a large batch of generated changes.

Code sample — .kiro/specs/review-aware-card/requirements.md

# Requirements

## Requirement 1: Visible review status

Acceptance criteria:

1. WHEN a sentence card is rendered THEN the system SHALL display `reviewStatus`.
2. WHEN `reviewStatus` is `draft` THEN the system SHALL show a learner-facing draft notice.
3. WHEN `reviewStatus` is `blocked` THEN the system SHALL not recommend the card for practice.

## Requirement 2: Tone safety

Acceptance criteria:

1. WHEN playful Filipino-English is rendered THEN the system SHALL show an Informal badge.
2. WHEN polite Tagalog is rendered THEN the system SHALL show when polite speech is safer.

Code explanation

  • Business logic: The requirements protect learners from confusing draft or informal language with final recommended speech.
  • Code logic: Each acceptance criterion can become a component assertion, data validator rule, or release check.
  • Expected result: Kiro can generate implementation tasks and tests that directly map to review and tone requirements.

Hands-on Lab D — Add schema validation for generated or curated card data

Developer action

  1. Create a validator script for sentence-card data.
  2. Run it manually.
  3. Ask Kiro to add missing validation rules.
  4. Wire the validator into the test or build command.

Kiro prompt sample

Create a data validation script for sentence cards.
Validate required fields, reviewStatus values, non-empty pronunciation, at least three grammar items, and informal label coverage for playful Filipino-English.
The script should fail with clear messages and be usable in npm scripts.

System design decision

  1. Validate data separately from UI: UI tests prove rendering, but they do not guarantee the whole dataset is complete. A dedicated validator catches missing fields across all cards before the app even renders. This is important when Kiro or developers add many cards quickly.
  2. Fail with actionable messages: A validator should tell developers exactly which card and which field failed. Clear failures reduce workshop friction and teach good release hygiene. Vague failures force developers to inspect data manually and slow down learning.
  3. Make validation scriptable: The validator must run from a command, hook, or CI job. Scriptability turns quality rules into repeatable engineering practice. It also lets Kiro reference the same command when creating hooks or release tasks.

Code sample — scripts/validate-cards.mjs

import { readFileSync } from "node:fs";

const raw = readFileSync("src/data/cards.json", "utf8");
const cards = JSON.parse(raw);

const allowedReviewStatus = new Set(["draft", "native-reviewed", "blocked"]);
const failures = [];

for (const card of cards) {
  const prefix = `Card ${card.id ?? "<missing id>"}`;

  for (const field of ["id", "englishInput", "naturalTagalog", "politeTagalog", "playfulFilipinoEnglish", "pronunciation", "reviewStatus"]) {
    if (!card[field]) failures.push(`${prefix}: missing ${field}`);
  }

  if (!allowedReviewStatus.has(card.reviewStatus)) {
    failures.push(`${prefix}: invalid reviewStatus ${card.reviewStatus}`);
  }

  if (!Array.isArray(card.grammar) || card.grammar.length < 3) {
    failures.push(`${prefix}: expected at least three grammar items`);
  }

  if (card.playfulFilipinoEnglish && card.playfulLabel !== "Informal") {
    failures.push(`${prefix}: playful Filipino-English must use playfulLabel = Informal`);
  }
}

if (failures.length > 0) {
  console.error(failures.join("\n"));
  process.exit(1);
}

console.log(`Validated ${cards.length} cards successfully.`);

Code explanation

  • Business logic: The validator enforces the educational contract for every card before release.
  • Code logic: It loads JSON, checks required fields, validates allowed review states, verifies grammar coverage, and fails the process on errors.
  • Expected result: Running npm run lint:data prints success for valid data or clear card-level errors for invalid data.

Hands-on Lab E — Use Kiro hooks for automated quality feedback

Developer action

  1. Create hook guidance in .kiro/hooks/.
  2. Ask Kiro to refine hook triggers and actions.
  3. Save a data or component file and observe the recommended validation workflow.
  4. Adjust the hook so it runs only relevant checks.

Kiro prompt sample

Create Kiro hook guidance for this workspace.
When source data changes, validate cards.
When React components change, run component tests.
When build scripts change, run the production build.
Each hook should summarize failures and suggest the smallest safe fix.

System design decision

  1. Context-specific checks: Running every check on every file change wastes time. Hooks should match file types to relevant validation: data changes trigger data validation, components trigger tests, and build scripts trigger release checks. This gives fast feedback without overwhelming developers.
  2. Automated coaching: In a workshop, hooks are not only automation; they teach workflow discipline. When a hook explains the failed card or missing label, developers learn what quality means in this project. Kiro becomes a reviewer and coach.
  3. Small safe fixes: AI-generated repair suggestions should be minimal. A hook should not rewrite the app when one card is missing pronunciation. Small fixes preserve developer control and keep diffs reviewable.

Code sample — .kiro/hooks/workspace-quality.md

# Hook: Workspace quality checks

## Data changes
Trigger: files under `src/data/`
Action:
1. Run `npm run lint:data`.
2. If validation fails, list the card IDs and missing fields.
3. Suggest the smallest JSON correction.

## Component changes
Trigger: files under `src/components/`
Action:
1. Run `npm run test:run`.
2. Summarize failed assertions.
3. Suggest minimal component or test updates.

## Build changes
Trigger: `package.json`, `vite.config.ts`, or files under `scripts/`
Action:
1. Run `npm run build`.
2. Summarize TypeScript or bundling errors.
3. Suggest the smallest safe fix.

Code explanation

  • Business logic: The hook keeps the learning product reliable while developers iterate quickly.
  • Code logic: The Markdown defines trigger/action behavior that Kiro can use as automation guidance inside the workspace.
  • Expected result: Developers receive targeted validation feedback when they edit data, components, or build configuration.

Hands-on Lab F — Add Kiro-assisted test generation and review

Developer action

  1. Ask Kiro to generate tests from the spec acceptance criteria.
  2. Review tests for meaningful assertions.
  3. Run tests and ask Kiro to explain failures.
  4. Add one negative test for blocked content.

Kiro prompt sample

Generate tests from the accepted requirements.
Do not create snapshot-only tests.
Test visible review status, blocked card behavior, informal badge, grammar section, pronunciation section, and polite guidance.
After writing tests, explain which requirement each test covers.

System design decision

  1. Tests from requirements: Tests should prove that requirements are implemented, not merely that current HTML matches a snapshot. Kiro can map acceptance criteria to assertions, helping developers keep tests aligned with product behavior.
  2. Negative tests matter: A blocked card should not be recommended for practice. Testing only happy paths misses safety behavior. Negative tests are especially important in educational apps where draft or blocked content must not be promoted.
  3. Explain coverage: Asking Kiro to explain which requirement each test covers makes the test suite easier to review. It also teaches participants how to evaluate AI-generated tests instead of accepting them as automatically sufficient.

Code sample — src/tests/card-policy.test.ts

import { describe, expect, it } from "vitest";

type Card = {
  id: string;
  reviewStatus: "draft" | "native-reviewed" | "blocked";
};

function isPracticeRecommended(card: Card) {
  return card.reviewStatus !== "blocked";
}

describe("card review policy", () => {
  it("does not recommend blocked cards for practice", () => {
    const card = { id: "blocked-001", reviewStatus: "blocked" } satisfies Card;
    expect(isPracticeRecommended(card)).toBe(false);
  });

  it("allows draft cards to appear with visible draft status", () => {
    const card = { id: "draft-001", reviewStatus: "draft" } satisfies Card;
    expect(isPracticeRecommended(card)).toBe(true);
  });
});

Code explanation

  • Business logic: Blocked content must not be recommended, while draft content can appear only when its status is visible elsewhere in the UI.
  • Code logic: The helper returns false only for blocked. The tests verify blocked and draft behavior.
  • Expected result: The test suite catches policy regressions if a future change recommends blocked cards.

Hands-on Lab G — Use MCP-ready context planning without adding runtime complexity

Developer action

  1. Ask Kiro to propose MCP server use cases without implementing them.
  2. Decide which external context would be useful in a future version.
  3. Write an integration note that keeps the current workshop local-first.
  4. Add a backlog item for future MCP integration.

Kiro prompt sample

Suggest MCP-ready integration options for a future version of this app.
Consider design files, content review sheets, issue trackers, and deployment metadata.
Do not implement the integrations now.
Create a backlog note that explains local-first scope for this workshop and future MCP opportunities.

System design decision

  1. Plan integrations without derailing the workshop: MCP can connect external tools and context, but the two-hour build should remain local-first. Planning future integration shows capability without adding setup complexity.
  2. Separate runtime app from engineering context: The learner-facing app does not need direct access to design tools or issue trackers. Those integrations are useful for development workflow, review, and planning. Keeping this separation protects app simplicity.
  3. Backlog as architecture memory: Writing future integration notes prevents ideas from being lost while keeping the current implementation focused. It also demonstrates how Kiro can help teams document engineering strategy.

Code sample — docs/mcp-backlog.md

# MCP-ready Backlog

This workshop remains local-first. No MCP server is required for the two-hour build.

Future MCP opportunities:

1. Connect design context so Kiro can align components with approved UI patterns.
2. Connect content review sheets so Kiro can read native-speaker review status.
3. Connect issue tracker context so Kiro can map bugs to specs and tasks.
4. Connect deployment metadata so Kiro can summarize release readiness.

Decision: keep runtime app independent from engineering integrations.

Code explanation

  • Business logic: The note captures future workflow improvements while preserving the workshop’s practical scope.
  • Code logic: This Markdown file is architecture documentation that Kiro and developers can reference later.
  • Expected result: Participants understand MCP-ready planning without needing external services during the workshop.

Hands-on Lab H — Ask Kiro for a final architecture review

Developer action

  1. Ask Kiro to compare implementation against steering and specs.
  2. Ask for risks, missing tests, and unclear content-review states.
  3. Ask Kiro to produce a final developer handoff note.
  4. Convert the review into backlog tasks.

Kiro prompt sample

Perform a final architecture review of this workspace.
Compare the implementation against steering files and specs.
Find gaps in review status visibility, informal labeling, mobile readability, tests, data validation, and release automation.
Return findings as severity, evidence, and recommended fix.
Then create a developer handoff note.

System design decision

  1. Kiro as reviewer: A final review uses Kiro’s codebase context to check alignment between intent and implementation. This is different from asking Kiro to generate more code. It teaches developers to use AI for architecture review, test review, and release confidence.
  2. Evidence-based findings: Findings should include evidence, not vague suggestions. Evidence might be a missing test file, a component without a badge, or a validator that ignores review notes. Evidence makes the review actionable and fair.
  3. Handoff note for continuity: Workshops often end with working code but weak documentation. A handoff note explains what was built, how to run it, what is known to be incomplete, and what should happen next. This makes the artifact useful after the session.

Code sample — docs/developer-handoff.md

# Developer Handoff

## Built in this workshop

- Kiro steering files for product, tech, structure, and language review.
- Kiro spec for sentence-card or generator workflow.
- Typed card model or Python content contract.
- UI renderer or static HTML renderer.
- Validation and test commands.
- Hook guidance for quality checks.

## Run commands

```bash
npm run dev
npm run test:run
npm run lint:data
npm run build

Known limitations

  • Language content is draft until native-speaker reviewed.
  • Runtime generation is intentionally out of scope for the first workshop build.
  • MCP integrations are documented as future backlog, not implemented.

Next tasks

  1. Add more reviewed cards.
  2. Add blocked-card UI behavior.
  3. Add deployment to AWS Amplify Hosting or Amazon S3.
  4. Add CI validation using the same local commands.

Code explanation

  • Business logic: The handoff preserves the learning and engineering outcome of the workshop.
  • Code logic: The Markdown captures built artifacts, commands, limitations, and next tasks in a format Kiro and developers can reuse.
  • Expected result: Another developer can open the repository, understand the current state, and continue safely.

Reference architecture notes

  • Kiro capabilities used in this workshop: agentic chat, spec-driven development, steering files, hooks, MCP-ready external context, privacy-aware workspace guidance, implementation tasks, documentation generation, and test generation.
  • Product scope: educational prototype for AWS Manila Community Day preparation. Language content must be reviewed by native Tagalog speakers before production usage.
  • Runtime scope: local developer workstation first. Optional AWS deployment can use Amazon S3 static website hosting or AWS Amplify Hosting after the workshop.