As someone involved in local festival activities in Aichi, I have repeatedly seen how difficult it is to find complete and reliable event information in one place.

Matsuri Schedule Catcher — Devpost Submission

Inspiration

Festival information across Japan's Tokai region is scattered across municipal websites, tourism associations, preservation groups, organizer pages, and individual announcements. Each source describes dates, venues, transportation, parking, rain policies, and cancellations differently. Finding a weekend event is difficult, and turning an announcement into searchable data requires repetitive manual work.

We wanted to make festival information easier to discover without treating AI output as automatically trustworthy. Our core principle is simple: AI can organize the announcement, but a person remains responsible for checking the result and its source.

What it does

Matsuri Schedule Catcher provides a searchable festival directory for Aichi, Gifu, Mie, and Shizuoka. Users can filter the fictional demonstration dataset by prefecture, date, festival type, and keyword, then open a detail page containing highlights, access, parking, rain policy, organizer, and source information.

The organizer workflow has two clearly separated modes:

  • Sample mode requires no code and makes no OpenAI request. It demonstrates the review flow with conservative rule-based extraction and leaves unknown fields empty.
  • Judge-only live mode sends an announcement to GPT-5.6 Luna through the OpenAI Responses API after the server validates a private judge access code.

Both modes lead to the same human review screen. Every field is editable. Required fields and formats are validated, potential duplicate records are shown as warnings, and nothing is added until the user confirms it. Confirmed records are stored in the current browser's localStorage.

How we built it

We built the MVP with Next.js App Router, React, TypeScript, Tailwind CSS, Zod, Vitest, the official OpenAI JavaScript SDK, the Responses API, and Structured Outputs.

The live server route accepts only JSON and validates request size, announcement length, the judge code, API configuration, and an in-memory rate limit. It then makes one non-retried request to gpt-5.6-luna with tools disabled, response storage disabled, minimal reasoning, and a 1,200-token output limit.

We use a dedicated, simplified Zod schema for OpenAI Structured Outputs. After receiving the structured response, the server replaces the official URL with the validated user input, adds the server date, and applies a stricter business schema for URL, date, time, length, array-size, and confidence validation. Schema failures return a safe SCHEMA_MISMATCH response rather than accepting questionable data.

The browser never receives the OpenAI API key or the configured judge code. Only human-confirmed records are written to localStorage. Twelve clearly labeled fictional records make the rest of the product testable without external services.

How we used Codex

Codex supported the project from requirements to submission preparation. We used it to clarify acceptance criteria, design the Next.js server/client boundary, implement the UI and filtering workflow, create the Zod schemas, add cost and abuse controls, write mocked OpenAI tests, improve responsive behavior, inspect production logs, manage Git and Vercel handoff, and prepare the Devpost documentation.

Codex was also used to diagnose the first live Structured Outputs failure. The business schema's URL validation generated format: "uri", which the API rejected for this Structured Outputs request. Codex helped separate the OpenAI transport schema from the strict post-validation schema, add regression tests, run the full quality gate, deploy the fix, and verify one successful production request.

The final automated gate includes lint, typecheck, 32 tests, a production build, and a client secret scan.

How we used GPT-5.6

GPT-5.6 Luna is the core live extraction engine, not the source of the fixed demo data. In judge-only live mode, it converts a festival announcement into structured JSON containing the festival name, location, venue, dates, times, summary, highlights, access, parking, rain policy, organizer, confidence, and warnings.

The integration uses the OpenAI Responses API and Structured Outputs. Unknown information is represented as null or an empty array instead of being invented. The model cannot replace the official URL, because the server overwrites that field with the validated user input before final validation.

We verified the production integration with one explicitly approved fictional announcement:

  • Model: gpt-5.6-luna
  • HTTP status: 200
  • Mode: live
  • Tokens: 657 input, 263 output, 920 total
  • Vercel openai_usage log: confirmed
  • API key and judge access code exposure: none

The successful result extracted “Tokai Matsuri Test 2026,” Aichi / Nagoya, Tokai Culture Plaza, August 8, 2026, 10:00–20:00, floats, taiko, Bon dancing, local food, the rain and cancellation policy, no dedicated parking, and the organizer.

Challenges we ran into

  • Preserving unknown values instead of allowing plausible but unsupported details.
  • Keeping a useful no-key sample experience unmistakably separate from live GPT processing.
  • Supporting a public hackathon demo without exposing the API key or judge code.
  • Limiting cost without background jobs, automatic retries, or extra model tools.
  • Handling Structured Outputs constraints while retaining strict URL, date, time, and range validation for application data.
  • Warning about duplicate community records without silently deleting or merging them.

Accomplishments that we're proud of

  • A complete search-to-source journey across mobile, tablet, and desktop layouts.
  • A real production GPT-5.6 Luna integration with Responses API and Structured Outputs.
  • Mandatory human review and editing before any extracted record is added.
  • Transparent sample and live modes with distinct badges and behavior.
  • Judge-code protection, input and byte limits, no retries, no model tools, and privacy-safe token logs.
  • A regression-tested schema split that keeps the OpenAI request compatible without weakening business validation.
  • 32 passing tests, successful lint and typecheck, production build, and client secret scan.

What we learned

Structured Outputs work best when the transport contract and business validation contract are treated separately. The model needs a schema it can reliably produce, while the application still needs strict validation before using that data.

We also learned that human review is not a fallback step; it is part of the product. Keeping official URLs under user control, making every field editable, and warning about duplicates creates a more honest workflow than presenting generated information as final.

Finally, a clearly labeled sample mode is more useful than silently substituting a mock when live AI is unavailable.

What's next for Matsuri Schedule Catcher

The next infrastructure step is a shared durable request-budget and rate-limit store, followed by a real editorial database and authenticated reviewer roles. After that, we would explore official-source freshness workflows, maps, favorites, notifications, and multilingual support.

Automatic publication, autonomous crawling, and unreviewed scraping are intentionally outside this MVP.

Built With

  • api
  • codex
  • css
  • gpt-5.6
  • luna
  • next.js
  • openai
  • outputs
  • react
  • responses
  • structured
  • tailwind
  • typescript
  • vitest
  • zod
Share this project:

Updates