Here, a verified app means you have checked its stated behavior and recorded the remaining limits. The example is a local practice app using invented data. Accounts, confidential records, payments, and public servers need checks that this example cannot provide.
You should be able to create a folder, save a text file, and open a terminal in that folder. Python runs the local server; Node.js is needed only for the downloadable automated checks. Git is optional for the first exercise. Treat the displayed time as a reading estimate; installation, building, and testing take additional time. Start with the local exercise, then use the later sections when your project gains external services or users.
Choose a small outcome and decide who could be affected
Start with a job you can describe and check. For this walkthrough, the job is to keep a personal log of project decisions: add a title, change its status, find it later, and export a backup. The example uses invented, non-sensitive records in one browser. It has no accounts, payments, shared database, or external service.
Write down what happens if the app fails. Losing a practice record is different from exposing a client's documents or sending a payment twice. That consequence determines the review you need.
| Project | Useful starting point | Before expanding it |
|---|---|---|
| Local experiment | Calculator, reading list, or this decision log with invented data | Check inputs, persistence, export, and recovery |
| Public app without accounts | A static checklist or calculator without confidential inputs | Review hosting, privacy, accessibility, abuse, and any server endpoints |
| Existing repository | One bug fix or interface change on a separate branch | Identify existing checks and inspect every changed file |
| Account or team app | A prototype with synthetic users and a written permissions model | Have an engineer review server authorization, data isolation, backups, and deployment |
| Payments or consequential decisions | A sandbox demonstration under domain supervision | Require qualified review of the real workflow and its failure handling |
A login, upload endpoint, secret, payment, or shared database changes the project design. Pause the implementation, update the requirements, and decide who can review that addition before giving the agent access to it.
Turn the idea into a brief the agent can follow
Download the project brief and read its twelve acceptance criteria. An acceptance criterion is an observable result: entering only spaces must leave the record count unchanged. A statement such as “make the app reliable” gives the agent no comparable test.
For your own project, record the intended user, the job, permitted data, environment, inputs, expected outputs, and exclusions. Resolve policy questions before implementation. Does an import merge or replace records? Can a title contain only spaces? What happens when saving fails? Those decisions belong in the brief.
| Decision in this example | Rule | How to check it |
|---|---|---|
| Required title | Trim whitespace; reject an empty result; maximum 120 characters | Try an empty value, three spaces, and a normal title |
| Imported records | Version 1, unique IDs, recognized fields and statuses | Duplicate one ID in an exported file and attempt import |
| Replacement | Validate first; confirm before replacing existing records | Cancel the confirmation and check that the old record remains |
| Storage failure | Keep the previous state and show a visible failure | Run the supplied simulated storage-failure check |
| Practice limits | At most 1,000 records; imports at most 1 MiB | Run the boundary checks and try a file exceeding the limit |
These limits are choices for this practice app. Set limits appropriate to your own job. Keep the current brief stable while implementing a slice; revise it deliberately when the product decision changes.
Prepare a folder, an AI coding tool, and a local browser
The shortest practice route is to download the Launch Decision Log starter app, unzip it into a new folder, and open that folder in a coding tool that can show file changes. Choose a separate copy for experiments. Keep an untouched copy so you can recover without asking the agent to reconstruct the app.
With a chat tool, save each returned file under its specified name; leave Markdown fences such as ```html out of the file. A coding agent can edit a folder directly and show the changes. If you use a visual builder, first check that you can inspect or export the code and control when it deploys. Those capabilities determine whether you can follow the review and recovery steps below.
Install Python 3 for the local server. If you plan to run the optional automated checks, also install Node.js. Open a terminal in the app folder and check Python before starting the server:
python3 --version
python3 -m http.server 8093 --bind 127.0.0.1
Keep this terminal running and open http://127.0.0.1:8093/ in your browser. If your Windows installation provides py instead of python3, use py -3 for the Python commands. If the command is missing, finish the installation and reopen the terminal. If the port is occupied, stop your previous development server or choose another port before entering practice records.
The explicit bind address keeps the server on this computer's loopback interface. Python's server otherwise binds to all interfaces and is intended for development rather than production hosting. Serve the app folder, which should contain no private documents or credentials. Press Ctrl+C in the server terminal when finished.
Keep the server terminal open. Use a second terminal for checks or other commands; the server occupies the first one until you stop it. If the browser shows a directory listing, check that index.html is directly inside the folder being served, rather than inside another unzipped folder.
Build the first journey from an empty folder
To practice starting with an idea, create a separate empty folder called decision-log-practice. Keep it separate from the downloaded example. The initial job is deliberately small: enter a title, add it to a list, and find it there after reloading. Editing, import, accounts, and deployment can wait.
Build a browser-local personal decision log in this empty folder.
Create index.html, styles.css, and app.js using plain HTML, CSS, and JavaScript.
Use no packages, remote assets, network APIs, accounts, or deployment.
First journey:
- A visible label identifies the Decision title field.
- Add decision trims the title and rejects an empty or whitespace-only result.
- A valid title, at most 120 characters, appears once in the list.
- Display user input as text, including strings that look like HTML.
- Save the list in localStorage and restore it after reload at the same address.
- Show a visible error if storage cannot be read or written. Do not replace
previous records or announce a successful save when writing fails.
Give me the complete files, the local run command, and the steps to check
these rules. Stop after this journey; do not add more features.Save the files or review the agent's file list, then start the loopback server from this new folder. If another practice server already uses port 8093, stop it first or use a different port. Open the matching address and perform the following checks on the code you received:
| Input or action | Expected result | If it fails |
|---|---|---|
Add Keep pilot local | One matching title appears | Report the click, input, visible error, and console error |
| Submit three spaces | No new entry; the existing title remains | Ask the agent to validate the trimmed value before saving |
Enter <img src=x onerror=alert(1)> | The characters appear as text | Stop using the page until text rendering is fixed |
| Reload at exactly the same address | The valid entries return | Check storage errors and the saved-data validator |
A working Add button is only the first check. If entries disappear after reload, send the repair request below with the exact steps you observed. After the fix, repeat the normal title, spaces, text-rendering, and reload checks. Record the input and result before you save a working copy.
For the next feature, ask: Add an Edit button for each entry. Saving an edit changes that entry without adding a duplicate. Cancel leaves the saved entry unchanged. Reuse the title validation and preserve all other behavior. Check both Save and Cancel, then reload. Add export before destructive features so you have a way to retain data. The downloaded app shows a completed version with these additional journeys; its automated checkers apply to that version, not to the files another AI generates for you.
Inspect a small change in the completed example
To practice reviewing a diff, switch to a copy of the downloaded app. Read SPEC.md, add one practice record, export it, and reload before requesting a change. You can now compare the agent's edits against a working version.
Use this prompt when working on the supplied project:
Requested change: replace the initial empty-list explanation with:
"Add a decision above. Export JSON before making major changes."
Keep the No decisions match explanation used by filters unchanged.
Read SPEC.md. Change only this explanation and preserve all acceptance rules.
Allowed implementation files: index.html, styles.css, app.js,
decision-model.js. Do not change SPEC.md or tests to make a failure pass.
Do not add packages, remote assets, network APIs, accounts, or new fields.
Ask before a change needs a new permission or changes how data is stored.
Keep text rendering safe. Validate the complete proposed list before saving it.
Only update the visible state after storage succeeds.
After editing, show the changed files and diff.
Run the supplied checkers if Python and Node.js are available.
Describe which browser journeys I still need to perform.
Report failures and checks you could not run.For this request, the diff should show the explanation changing in app.js, with no changes to import, storage, or tests. Open an empty practice store to check the new text, then create a record, search for a missing title, and check that the filter explanation still says to change or clear filters. If unrelated files changed, restore your practice copy and repeat the narrower request.
In your own app, repeat the affected journey and its closest failure case after each feature. If an edit changes title validation, check spaces and reload again. If it changes import, check cancellation and rejection of damaged files again.
Keep a recoverable version before the next request
Git retains passing source versions. For a new copy of the downloaded app, check that you are in the intended folder and review the file list before making the first commit:
git init
git status --short
git add README.md SPEC.md index.html styles.css app.js decision-model.js tests
git commit -m "Save passing local prototype"
For the three-file app built from scratch, replace the git add line with git add index.html styles.css app.js. Add only files that exist and that you have reviewed. If Git asks for an author identity, configure it for this repository before committing, or use the folder-copy approach below.
After an AI edit, run git status --short and git diff. Review code, configuration, tests, and dependency files separately. Commit the intended files after the checks and browser journey pass. Git records source files; your browser records need their own JSON export.
If you already have a repository, use its existing process and a separate branch instead of initializing another repository. Preserve any work that was present before the agent started. The repository change-boundary guide explains a more detailed workflow for shared projects.
If Git is unfamiliar, duplicate the entire practice folder before each change and label the last passing copy. To undo a failed experiment, return to that copy. Avoid destructive Git recovery commands while you are unsure which uncommitted files they would remove.
Run the browser journeys yourself
Use invented records. Record the input, observed result, browser, date, and source version. A passing static checker does not exercise buttons, browser validation, file selection, confirmation, or reload behavior.
| Journey | What to do | Expected result |
|---|---|---|
| Create | Add “Keep pilot local” with an owner and status | One complete record appears |
| Empty and whitespace | Try an empty title, then three spaces | No new record; the normal record remains |
| Text rendering | Use <img src=x onerror=alert(1)> as a title | The characters display literally; no image or script runs |
| Edit | Change the normal title | Its record changes; the count stays the same |
| Filters | Search a title, select a status, then clear filters | Only matches appear, then the complete list returns |
| Persistence | Reload using the same address and browser profile | Valid saved records return |
| Export | Export JSON and open the downloaded file in a text editor | Version 1 and the expected records are present |
| Invalid import | Duplicate an ID in a copy of the export; try malformed JSON too | A visible error; existing records remain unchanged |
| Valid import | Import the unchanged export; cancel once, then confirm | Cancel preserves state; confirmation replaces it with the valid file |
| Delete | Cancel deletion, then confirm deletion of one practice record | Cancel preserves it; confirmation removes only that record |
| Keyboard and narrow layout | Use Tab and keyboard controls; test at 320 CSS pixels | Controls remain labeled and operable, focus is visible, and content fits |
| Console and network | Inspect browser developer tools while repeating the journey | No unexplained errors or external requests; local page assets are expected |
Check the complete journey after adding a feature. A working submit button followed by missing records after reload is a failure. Width measurements and visible labels cover only part of accessibility; they do not replace screen-reader and other assistive-technology testing. See the W3C form-label guidance when reviewing how controls obtain their names.
Check failures that a polished interface can hide
An HTML required attribute can accept spaces in a text field. If the submit handler trims those spaces into an empty title, it must reject the result before saving. Otherwise the record can be written successfully and fail a stricter validator on reload. The supplied app uses the same validation function for new records, imported files, and stored data.
Imported IDs also need uniqueness. If two records share an ID, an edit can target the wrong one and an ID-based deletion can remove both. The import validator rejects that file before it touches storage. Try the duplicate-ID case even when every individual field looks valid.
Storage can fail after validation. If browser storage is full or blocked, the previous records should remain and the page should show a save error. This app prepares the proposed list, validates it, writes it, and only then displays it as saved. Its automated checks simulate a failed write. Repeat that check if you change how saving works.
Oversized imports need a limit before parsing, plus limits on the parsed structure. This practice app checks the file size before reading it and permits up to 1,000 records. Those limits reduce avoidable work in this small workflow; they do not constitute a general defense for a public upload service.
When you add a regression test, make sure it can detect the defect. For the whitespace bug, temporarily remove the rejection in a disposable copy and confirm that the test fails. Restore the fix and confirm that it passes. The optional supplied checks automate this exercise for three defects.
Understand where data lives and how to recover it
The app uses browser localStorage. Records belong to the page's origin and browser profile. Changing the protocol, host, or port can show a different store: localhost and 127.0.0.1 are different hosts. Return to the original address before assuming the data was deleted.
Private browsing has a shorter storage lifecycle, and storage policies can block access. Opening the HTML directly as a file can also produce browser-dependent storage behavior. Use the local server and a consistent address. MDN documents these storage boundaries.
Export a backup before a major edit or replacement import. Check its version and record count. Keep a separate copy, then rehearse restoring it using a disposable browser profile. Confirm that titles, notes, statuses, and counts return. A file that downloads successfully but cannot be restored is not a useful backup.
If saved data is unreadable, the app shows a recovery warning and disables ordinary editing. It preserves the original stored value and offers Export saved data for recovery. Save that file separately before importing a known-good backup. A recovery export can contain malformed data; its purpose is to preserve material for inspection, not guarantee an automatic restore.
If saving fails, keep the current page open. Copy any unsaved title or notes into a separate text file, export the previously saved records, and investigate storage settings or quota. Retrying an operation should not require deleting the previous data. Never use this example to store passwords, API keys, client records, health information, or other sensitive material.
Give the agent a reproducible bug report
Send the smallest case that demonstrates the mismatch. Include the command, browser address, exact input, expected result, actual result, and relevant error text. Remove private values before sharing logs.
Reproduce this defect before editing:
1. Add one valid decision.
2. Submit a title containing three spaces.
3. Reload at the same address.
Expected: the whitespace submission creates nothing; the valid record remains.
Trace validation through submit, persistence, and reload.
Add a regression check for this journey without weakening existing checks.
Change only the code necessary to fix it. Run the affected test and the
nearby passing journeys. Show the diff and unresolved cases.The completed example already rejects this whitespace case. For a defect in your generated app, replace the report with what you observed. If the agent cannot reproduce it, ask what information is missing. A console error may identify a file and line, but it does not establish the intended behavior. Avoid approving a broad rewrite merely because the original failure is difficult to explain.
Stop a repair attempt if the agent cannot explain the failure, starts changing unrelated features, or proposes discarding saved data. Return to the last working copy, preserve the failing input, and ask for a smaller reproduction or an engineer's help. Set a limit on automated repair attempts and tool spending before starting a long unattended run.
Inspect changes to tests. Removing an assertion, skipping a case, or replacing a specific expectation with “no error” can hide the defect. GitHub's AI-generated code review guidance includes dependency checks, ignored constraints, and deleted tests. The site's unit-testing guide develops that workflow further.
Check what the coding agent is allowed to do
Give the agent the practice folder and only the tools needed for the current change. Before widening access, ask what it needs to read, what it will change, and where any data will go. Keep production credentials and personal documents out of the practice folder. OWASP's agent security guidance covers tool access and review of sensitive actions.
| Request from the agent | What to check before continuing |
|---|---|
Read a real .env or credentials file | Use a redacted example with variable names. Grant access only if the real value is needed for an authorized task and the environment protects it. |
| Access the home directory or another repository | Identify the required file. Narrow access to that location instead of approving the whole directory. |
| Run a command that downloads a script into a shell | Find the official installation instructions and inspect what will execute. Do not approve an unexplained command because it promises to fix setup. |
| Push, deploy, migrate, or delete | Review the target, diff, affected data, and recovery plan. Keep unattended execution disabled for actions you cannot assess. |
Files and web pages can contain instructions aimed at an agent. A README asking it to upload credentials or disable tests is content to inspect, not permission from you. Review project instructions before adopting them, and stop tool execution if outside content redirects the task toward private files or unrelated actions. A prompt alone cannot enforce this: use tool permissions and review sensitive actions. OWASP describes indirect prompt injection and least-privilege controls.
If the agent proposes a package, ask why the existing browser or language features cannot do the job. Verify its exact name in the official registry, follow the linked repository, and check maintenance and licensing. Review changes to the lockfile, which records resolved dependency versions, and ask about installation scripts. A package scan can identify known problems; it does not prove that a package is trustworthy.
Generated output needs handling appropriate to where it runs. Displaying a title with textContent addresses markup interpretation in this interface. SQL values, shell arguments, and file paths have different rules. OWASP's output-handling guidance explains why the destination matters.
Keep secrets out of browser code and handle exposure
A secret used in frontend JavaScript can be delivered to every visitor. Putting it in an environment file does not necessarily keep it private: Vite's documentation, for example, says that VITE_* values are bundled into client code. Use a clearly fake marker in a test build and look for it in the generated files and browser developer tools. Never use a real secret for that experiment.
Privileged API calls need a server operation with authentication and authorization appropriate to the task. Store credentials in the server's secret configuration, restrict their permissions, and check that logs and error responses do not return them. Moving a key to a server while leaving an unrestricted proxy endpoint can still expose the service to misuse.
If a real key enters a prompt, repository, public build, or log, treat it as exposed. Revoke or rotate it through the provider, update the authorized server configuration, and review available usage logs for unexpected activity. Remove it from the exposed location and prevent another build from publishing it. GitHub recommends revoking or rotating leaked credentials first. Deleting the current file does not revoke the key or remove earlier commits; coordinate any repository-history cleanup with collaborators.
Change the checks when the app gains a backend or public users
A public app without accounts
If you only publish static files, check that the upload contains no private files, secrets, or development-only material. A public URL does not make browser storage shared across users; this example still stores each browser's records locally.
If the app accepts submissions on a server, list every endpoint and external service. Check that the server validates inputs independently of the browser and uses parameterized database queries, which keep values separate from query text, where applicable. Limit requests that consume resources and review error responses for private data. Test the deployed URL with invalid input and a fresh session, and retain a rollback version.
Accounts and shared data
Write a table of roles, actions, and records each role may access. Use two synthetic accounts: create a record as account A, then attempt to read or modify it as account B by changing the record identifier in a request. Check direct API access, not just hidden buttons. Decide whether the intended response is a denial or a deliberately shared record. Authentication identifies a user; authorization decides what that user can do. Follow OWASP's authorization guidance and have an engineer review the server enforcement.
Repeat the access check for update, delete, export, and uploaded attachments. Test a signed-out request too. A hidden button is insufficient if a direct request still succeeds. Record the intended result for each role so that deliberately shared records are distinguishable from accidental exposure.
Uploaded files
The local JSON importer is not a model for accepting arbitrary files on a public server. Define allowed file types, content validation, size and count limits, generated storage names, and a location where uploads cannot execute as application code. Decide who can retrieve and delete a file. Review malware handling and document processing appropriate to the format. OWASP's upload guidance provides a separate checklist.
In a test environment, try an allowed file, a file with an allowed extension but different content, an oversized file, and retrieval by another user. Rejected files should not become downloadable or overwrite another file. Ask the reviewer to show the storage location and the access check used when serving a file; accepting an upload safely and serving it safely are separate checks.
Payments and other outgoing actions
Use the provider's sandbox with synthetic data. Test a double click, a timed-out response, and retrying the same action. A timeout does not tell you whether the provider completed the operation. Decide how the app checks the provider's status before issuing another request.
For operations the provider supports, use its idempotency mechanism so that a retry identifies the same operation. Stripe documents how its idempotency keys work; follow the actual provider's rules rather than assuming every API behaves alike. Check that repeated attempts produce the intended single operation and that the app records its final status. Review duplicate callbacks or webhook events as well.
Before using real funds or sending real messages, have a qualified reviewer check server permissions, retry handling, and how an operator can disable the integration. Preserve transaction identifiers for investigation while keeping credentials and unnecessary personal data out of logs.
Database changes and background work
Inspect migrations before running them. Identify tables and data affected, test on a copy, take an appropriate backup, and rehearse recovery. Rolling back application code does not automatically reverse a data migration. For background jobs, specify what happens after a partial failure or retry, how duplicate work is prevented, and how failures reach an operator. These decisions require knowledge of the actual database and deployment.
On a staging copy, interrupt the job partway through and retry it. Check that completed work is not applied twice and incomplete work is either resumed or reported. Restore a backup into a separate environment and verify record counts plus a few important relationships. A backup job reporting success is insufficient if nobody has checked a restore.
Check the deployed app and rehearse recovery
Use a test deployment with invented records before publishing for other people. Keep production credentials, data, and payment settings separate. Write down the source version, deployment target, settings it requires, and the last working version you can restore.
- Open the deployed URL in a fresh browser profile. Confirm that scripts and styles load, create a practice record, and repeat an invalid-input case. If there is a backend, verify that the request reaches the intended test service.
- Reload and check persistence. For this local-storage example, a new domain or port has a separate store. Export at the original address and import into the new one after checking the backup; do not assume hosting moves browser data.
- Test settings that differ from local development: API addresses, sign-in callbacks, allowed origins, and server secret configuration. Use browser network details and server logs to locate failures. Redact private values before sending logs to the agent.
- Practice restoring the prior code release in the test environment. Repeat a short create-and-reload check afterward. If the new release changed stored data or the database, test its separate recovery plan too.
- Decide who will see errors or failed jobs, who can stop a harmful integration, and how to contact that person. For a paid API, configure the provider's available spending controls and inspect unexpected usage before expanding access.
If the page is blank, check failed script requests and the first console error before asking AI to rewrite the interface. If requests point at the wrong service, inspect deployment configuration. If data is missing after changing the address, check the original browser store before attempting a destructive reset. An engineer should choose the recovery procedure when a release has changed shared data; restoring old code alone may leave the changed database in place.
Decide whether the project is ready for its intended users
For each area below, record what you tried and what happened. Leave unperformed checks marked as unperformed. Decide readiness for the intended use: a personal practice app and a service holding other people's data need different review.
| Area | Evidence to collect | Reason to pause |
|---|---|---|
| Requirements | The input and observed result for each requirement | Unresolved replacement, permissions, or deletion policy |
| Function and failure | Normal journeys plus invalid input and failed saves | Corruption, disappearing records, or tests changed to hide failures |
| Security | Review of input handling, dependencies, credentials, and authorization | Unknown package, exposed secret, or untested access boundary |
| Accessibility | Keyboard, focus, labels, errors, layout, and relevant assistive testing | A control cannot be identified or operated |
| Data | Storage location, export, deletion, backup, and rehearsed restore | No viable recovery for data people depend on |
| Operations | A fresh-session deployment check, error reporting, practiced recovery, and a contact | No way to detect or stop a harmful release |
The downloadable example is a local prototype. Its tests do not cover accounts, a backend, production hosting, payments, multi-user permissions, a cross-browser matrix, or a screen-reader session. Keep that boundary visible when showing it to someone else.
For expert review, provide the brief, changed files, dependencies, test commands and results, unresolved cases, data flow, deployment plan, and recovery procedure. Give the reviewer a specific question, such as whether account B can access account A's records. The code-review workflow helps prepare that packet; AI-assisted review still requires a person who can assess the consequences.
Optional: run the automated checks for the downloaded example
Install Node.js if you have not already done so. In a second terminal inside the downloaded app folder, run:
node --version
python3 tests/static_gate.py --mutations --json
node tests/runtime_gate.js --mutations
On Windows, substitute py -3 for python3 if that is your Python command. The Python result should include baseline_passed: true; the Node result should include passed: true and 22 passing checks for validation and storage. Neither command opens the browser. If a command fails, retain the output and check whether your edits changed a required rule.
The --mutations option also introduces deliberate defects in temporary copies. The source checker rejects three source defects; the Node checks reject whitespace titles, duplicate IDs, and displaying changed data before saving succeeds. This checks whether the tests detect those specific mistakes. It does not establish comprehensive security.
The dated test record separates those checks from browser observations. All twelve acceptance entries have supporting results. The browser run exercised replacement-import cancellation and confirmation, reload after restoration, and selection of malformed, oversized, and duplicate-ID files. Storage exceptions and unreadable saved data were simulated separately in Node; no backend, payment, screen-reader, or cross-browser testing is claimed.
If you need to check that the ZIP matches this result record, download the archive and test-record checker beside the ZIP and JSON, then run:
python3 vibe-coding-release-gate.py \
vibe-coding-test-results.json \
vibe-coding-starter-app.zip \
--mutations
It checks the archive's identity, reruns the source and storage checks, and checks the result record for missing or failed entries. It cannot authenticate a written browser observation or test your edited app. After changing the code, repeat the affected browser journeys and keep a new record with that source version.
