Kiro IDE: Creating a First Project — Part 3
From reviewed specifications to an implemented manufacturers browser: inspecting generated files, completing missing behavior, and testing the result.
When the plan meets the generated files
Part 1 established steering context. Part 2 developed and reviewed the feature requirements, technical design, and task plan. This stage examines what happened when implementation tasks were run.
The objective remains a single screen for browsing vehicle manufacturers from the NHTSA vPIC API. Users move between pages using Previous and Next controls, with readable loading messages and a way to retry failed requests.
An execution limit changed the workflow: the available free Kiro credits were exhausted before the application was complete. The next step was to inspect the actual artifacts, identify the missing work, and continue from the specification.
This article distinguishes the Kiro-generated starting point from the subsequent completion work. It does not present the final application as an uninterrupted Kiro implementation.
1. Inspect the artifacts before deployment
The supplied files contained a semantic HTML skeleton, responsive CSS, and an empty JavaScript file.
| Supplied file | Observed state | Consequence |
|---|---|---|
index.html | Page title, pagination controls, status region, and table | The interface structure exists |
css/style.css | Mobile-first styling and a 600px breakpoint | The basic presentation exists |
js/app.js | 0 bytes | No requests, rendering, navigation, or retry behavior |
Uploading this starting point would display an empty table and a label reading “Loading…”, with both buttons disabled. A script tag pointing to a file is not evidence that application behavior has been implemented.
The starting files are still useful evidence for a technical tutorial. They show a partially executed plan and make the remaining work concrete. They are not sufficient for deploying a functioning manufacturers browser.
2. Continue from the specification with an explicit handoff
Codex completed a separate deployment copy after the credit limit was reached. The original repository files were preserved. This provides a clear boundary between generated work, reviewed work, and later changes.
| Contributor | Work represented in this handoff |
|---|---|
| Kiro | Steering and specification workflow; supplied HTML and CSS starting point |
| Codex | Artifact inspection, JavaScript implementation, focused HTML/CSS adjustments, local browser verification, and this tutorial |
| Deployment operator | Upload, production-origin integration checks, and release decision |
The reviewed specification remained the baseline. Completing the application did not require introducing a framework, a database, or a build system.
manufacturers-browser/
├── index.html
├── css/
│ └── style.css
├── js/
│ └── app.js
└── DEPLOYMENT.mdThe deployment notes describe provenance, verification evidence, remaining integration checks, and known limitations. They are handoff documentation rather than a runtime dependency.
3. Implement the request and display loop
The completed script automatically requests page 1. A successful response is validated before it replaces the table, and each navigation request retrieves one API page.
The official Get All Manufacturers reference documents 100-item pages and the page parameter. The application does not rely on 232 as a fixed final page and does not display an unverified total. NHTSA vPIC API reference
Open page → request page 1 → validate response → render table
Next → request current page + 1 → validate → replace results
Previous → request current page - 1 → validate → replace results
Failure → preserve displayed results → offer RetrySeparate state values represent the displayed page, requested page, and failed target. The distinction becomes important when page 2 is visible but the request for page 3 fails: the table and label must continue to describe page 2, while Retry must request page 3.
The code is contained in an IIFE. It uses native browser features and adds no third-party runtime dependency.
4. Preserve state when requests fail
The request handler uses a single try/catch/finally structure. A timer remains active while the response body is read, and cleanup runs after success or failure.
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error('HTTP error: ' + response.status);
const data = await response.json();
validate(data);
// Apply the validated response to the interface.
} catch (error) {
// Preserve results and record the failed target for Retry.
} finally {
clearTimeout(timeout);
state.loading = false;
updateButtons();
}This excerpt illustrates the control flow; the delivered script contains the full handlers and user messages. AbortController can cancel both the fetch and consumption of the response body, which matters when headers arrive but the body stalls. AbortController documentation
The delivered timeout is 10 seconds. A failed initial load leaves both navigation buttons disabled and offers Retry. A later failure preserves the previously displayed table. Repeated retries retain the same failed target until a successful response is received.
Next and Previous are evaluated independently. Both are disabled during an active request, preventing overlapping navigation from replacing results out of sequence.
5. Validate data before replacing results
A successful HTTP status and valid JSON do not establish that a response is usable. The implementation checks that Results is an array, Count is an integer between 0 and 100, and the count matches the array length.
Record validation checks the fields needed for display, including a non-empty full manufacturer name and an array of vehicle types. Malformed records produce a recoverable response error rather than being silently discarded. This resolves the validation policy identified at the end of Part 2.
Each displayed cell is created as a DOM element and assigned text:
const cell = document.createElement('td');
cell.textContent = value;Manufacturer names and other API fields are therefore displayed as text rather than interpreted as HTML. The automated checks include a name containing an HTML-like payload and verify that no image element is created.
The name column uses the common name when non-empty and falls back to the full name. Null countries and empty vehicle-type arrays display as empty cells.
6. Apply pagination boundaries without inventing a total
Boundary handling follows the reviewed application assumption that API pages form a contiguous sequence:
| Response | Behavior |
|---|---|
| 100 records | Display results and permit probing the next page |
| 1–99 records | Display results and record the boundary |
| Empty page after a full page | Preserve displayed results and disable Next at that boundary |
| Failed or invalid response | Preserve results and provide Retry |
A full page does not prove another populated page exists. An empty probe is the expected discovery case when the final populated page contains exactly 100 records.
Navigating backward to an earlier full page preserves the later boundary. A full response at or beyond a previously recorded boundary can invalidate it. These rules are a practical browsing strategy, not a guarantee of snapshot consistency while the upstream registry changes.
The label remains “Page N”. The empty initial dataset has a separate “No data” state.
7. Complete presentation and accessibility behavior
The Kiro-generated markup already included native buttons, column-header scope attributes, and a status live region. The completion work retained that structure and added request-state signaling through aria-busy.
Focused CSS adjustments allow column headings to wrap, strengthen keyboard focus outlines, and keep navigation and retry targets at least 44 pixels high. The status region remains present, and successful loads provide an explicit message instead of relying on silence to signal completion.
After user-triggered navigation renders new records, focus moves to the first data cell. Initial automatic loading does not move focus into the table.
These are implemented behaviors, not a claim of comprehensive accessibility certification. Screen-reader announcements and navigation should still be checked manually in the deployed application.
8. Verify the behavior with controlled responses
Local checks ran in headless Chrome against a controlled HTTP endpoint. The test server supplied representative success and error responses; it did not query the live NHTSA registry.
Ten behavioral and layout checks passed, and an additional check captured the application screenshot used in this article.
| Verification scenario | Result |
|---|---|
| Full initial page, fallback names, and safe text rendering | Passed |
| Forward/back navigation, partial boundary, and focus | Passed |
| Empty probe preserves results and boundary through backward navigation | Passed |
| Initial failure disables navigation; Retry requests page 1 | Passed |
| Repeated retries request the failed page | Passed |
| Malformed JSON produces a recoverable error | Passed |
| Invalid count or malformed record preserves the displayed results | Passed |
| Stalled response body times out and releases loading state | Passed |
| Empty initial dataset disables navigation | Passed |
| 320px layout and 44px navigation targets | Passed |
The timeout check used a test-only 350ms limit to exercise cancellation quickly. The delivered application retains the 10-second setting. The screenshot shows seven fixture records, including “Example Motors”; it is not a capture of live registry data.
Browser checks establish evidence for these scenarios. They do not establish production CORS access, cross-browser coverage, or screen-reader behavior.
9. Upload to staging and verify the real integration
The completed copy is ready for a staging upload. Preserve the relative paths when uploading index.html, css/style.css, and js/app.js into a dedicated public subdirectory.
Use a staging directory first so the existing traffic endpoint is not replaced accidentally. Open the uploaded page over HTTPS and check the first load, Next, Previous, and browser console.
The JavaScript defaults to direct NHTSA access with USE_PROXY = false. Production-origin CORS has not been verified in this handoff. If browser access is blocked, deployment is incomplete until a same-origin PHP proxy that accepts and forwards the page parameter is implemented and verified. The package does not include that conditional proxy; do not enable the switch before one exists.
No build step or Node server is needed for the deployed application. The local browser-test tooling is separate from its runtime files.
Where Part 3 ends
The initial Kiro output was a useful interface skeleton, but an empty JavaScript file made it unsuitable as a functioning deployment. A documented handoff completed the missing behavior, retained the minimal stack, and produced passing local checks.
The result is a staging candidate with explicit evidence and limits. Production integration remains a separate checkpoint, and this article does not claim that a live deployment has been completed.
The engineering lesson is practical: inspect the generated artifacts, trace the remaining work back to the specification, verify concrete behavior, and keep authorship and deployment status clear when execution continues across tools.
Project repository
All project files—including the steering documents, feature specifications, and deployment files—can be found in the kiro_traffic repository on GitHub.