Skip to content

Worked Example: Codex + Spec Kit

Spec-Driven Development explains the five commands. This page is the other kind of explanation — one feature, run for real, so you can see exactly what appears on screen at each step before you try it yourself. It uses Codex CLI ($speckit-*); everything here works identically in Claude Code with /speckit-* instead.

A real page in the app itself — not a separate script. A new OSINT Check entry in the sidebar takes you to a page with a text box: type a username, click Check, and the page shows whether that username has a public profile on GitHub, Reddit, and Hacker News.

Under the hood that’s one new backend endpoint (behind the same login every other route already requires, making one plain GET request per platform to that platform’s public profile URL) and one new frontend page that calls it. No new database table — nothing about a check gets saved, it’s a live lookup every time. This is a deliberately small first feature: one endpoint, one page, but it exercises the same browser → API → UI shape as almost everything else you’ll build on this template.

Terminal window
codex

Assume /speckit-constitution has already been run once for this repo (see Spec-Driven Development if not) — you only need to do that once per project, not once per feature.

$speckit-specify Add an "OSINT Check" page to the dashboard. A logged-in
user types a username and clicks Check. The page calls a new backend
endpoint that checks whether that username has a public profile on
GitHub, Reddit, and Hacker News (one plain, unauthenticated GET request
per platform to that platform's profile URL — no scraping behind login),
and shows FOUND/NOT FOUND per platform. Nothing gets saved to the
database — every check is live.

Codex creates a new branch — something like 004-osint-username-checker — and writes specs/004-osint-username-checker/spec.md. Trimmed down, it looks like this:

# Feature Specification: OSINT Username Checker
**Feature Branch**: `004-osint-username-checker`
**Status**: Draft
## User Scenarios & Testing
### User Story 1 - Check a username from the dashboard (Priority: P1)
A logged-in user opens the OSINT Check page, types a username, and
clicks Check. The page shows whether that username has a public profile
on GitHub, Reddit, and Hacker News.
**Independent Test**: Log in, go to the OSINT Check page, enter a
known-existing username, click Check, confirm the page shows FOUND for
the platforms where that username is real.
**Acceptance Scenarios**:
1. **Given** a logged-in user on the OSINT Check page, **When** they
enter a username that exists on all three platforms and click Check,
**Then** the page shows FOUND next to each platform.
2. **Given** a logged-in user, **When** they enter a username that
exists nowhere and click Check, **Then** the page shows NOT FOUND
next to each platform, with no error.
### Edge Cases
- What happens when a platform's request times out?
- What does the page show while a check is in progress?
- What happens if Check is clicked with an empty username field?
## Requirements
### Functional Requirements
- **FR-001**: System MUST provide an authenticated API endpoint that
checks a single username against GitHub, Reddit, and Hacker News
public profile URLs.
- **FR-002**: System MUST provide a page, reachable from the sidebar,
with a text input and a Check button.
- **FR-003**: System MUST display a FOUND/NOT FOUND result per platform
after a check completes.
- **FR-004**: System MUST show a loading state while a check is in
progress, and disable the Check button until it completes.
## Success Criteria
- **SC-001**: A logged-out user cannot reach the check endpoint
directly.
- **SC-002**: A real, existing username correctly shows FOUND for that
platform.
- **SC-003**: A full check (all three platforms) completes in under 10
seconds under normal network conditions.
## Assumptions
- No results are stored — every check is live, nothing persisted to the
database.
- Public profile pages only, no authentication or API keys against the
checked platforms.

This is the step it’s easiest to skip and shouldn’t be. Read it looking specifically for scope that crept in on its own — here, a wrong version might have invented “save your check history” or “check 15 platforms,” neither of which was asked for. This one matches what was described: one endpoint, one page, three platforms, nothing saved. If it didn’t match, you’d fix the wording and run $speckit-specify again rather than let a wrong assumption ride into the plan.

$speckit-plan Backend: a new GET /api/v1/osint/check/{username} endpoint
in app/api/routes/osint.py, behind the existing CurrentUser auth
dependency, using httpx (already a dependency) with a 5-second timeout
per platform. Response models go in app/models.py, following the same
pattern as Message — plain SQLModel classes, no table=True, since
nothing gets persisted. Frontend: a new authenticated route at /osint
(frontend/src/routes/_layout/osint.tsx) with a text input and a Check
button, calling the generated OsintService via TanStack Query. Add an
"OSINT Check" entry to the sidebar in
frontend/src/components/Sidebar/AppSidebar.tsx.

This writes specs/004-osint-username-checker/plan.md — technical approach and file layout. There’s no data-model.md, because there’s no database entity involved, just response schemas.

$speckit-tasks

This writes tasks.md, organized around the one user story from the spec. A real one looks roughly like:

## Phase 1: Setup
- [ ] T001 Create app/api/routes/osint.py with an APIRouter (prefix
"/osint", tag "osint")
## Phase 2: Foundational
- [ ] T002 Add OsintPlatformResult and OsintCheckResponse response
models to app/models.py (plain SQLModel, no table=True)
- [ ] T003 Register osint.router in app/api/main.py
## Phase 3: User Story 1 - Check a username from the dashboard (P1)
- [ ] T004 [P] Implement check_platform(username, url_template) in
osint.py using httpx, with a 5-second timeout, returning a bool
- [ ] T005 Implement GET /check/{username} behind CurrentUser, calling
check_platform for GitHub, Reddit, and Hacker News
- [ ] T006 Regenerate the frontend client: bash scripts/generate-client.sh
- [ ] T007 [P] Create frontend/src/routes/_layout/osint.tsx with a text
input, Check button, loading state, and a FOUND/NOT FOUND result
list wired to OsintService via TanStack Query
- [ ] T008 [P] Add "OSINT Check" to frontend/src/components/Sidebar/AppSidebar.tsx
**Checkpoint**: Logged in, the OSINT Check page lets you type a
username, click Check, and see FOUND/NOT FOUND per platform

Notice tasks map directly back to the functional requirements in the spec — that traceability is the point of running these in order instead of jumping straight to implement. T006 is the easy one to forget by hand: the frontend can’t call an endpoint it doesn’t know exists yet, so the client has to be regenerated between writing the backend route and writing the page that calls it.

$speckit-implement

Codex works through tasks.md top to bottom — backend route, then the client regeneration, then the frontend page and sidebar entry — checking off each task ([ ] → [x]) as it completes it. Watch this one run rather than walking away, especially the first few times: it’s the fastest way to build a sense for what “normal” looks like, so you notice when a future run does something odd.

Both dev servers from Getting Started should still be running. Refresh the app in your browser, log in if you’ve been logged out, and you should see a new OSINT Check entry in the sidebar. Click it, type in a username, and hit Check.

(octocat is GitHub’s own mascot/test account — a safe, real username to try first.) Expect something like:

GitHub: FOUND
Reddit: NOT FOUND
HackerNews: NOT FOUND

Same “explain it back” rule as any other AI-generated code — a few concrete things worth checking on this one before you trust it:

  • Hit the endpoint directly while logged out (or with an expired token) — does it actually reject you, or did the auth dependency get left off?
  • Does the button disable (or show a spinner) while a check is running, or can you fire off five requests by clicking fast?
  • Does a slow/unreachable platform hang the whole page, or does the 5-second timeout actually apply?
  • Does submitting an empty username show a sensible message instead of a raw error?
Terminal window
git add -A
git commit -m "Add OSINT username checker"
git checkout main
git merge 004-osint-username-checker

If you used a git worktree for this feature, remove it now too: git worktree remove ../osint-username-checker.

One backend endpoint behind auth, one frontend page, no new database table — small enough to read start to finish in one sitting. But it’s still a real full-stack feature: browser → API → response → UI, the same shape as nearly everything else you’ll build here. That’s the rep worth doing before you point this loop at something bigger.