New to OpenCode? Start with OpenCode setup, model providers and course budget, then return here for your chosen app.

"Which experiments are still waiting?" should not need a spreadsheet hunt

Jules runs a small lab. Experiments live in a spreadsheet, a whiteboard and a few emails, so nobody can see at a glance what is planned, what is running and what is late. Lab Board is a one-file app that shows every record in a column for its status, lets you move it forward, find it again, flag overdue work and keep a real backup file.

You will build it with OpenCode, one request at a time, and check every step against known answers. The app runs locally in your browser: one file, no installation, no upload. The eight practice records are synthetic: invented experiments and first names. Your own permitted records come in at Step 15.

The real result: a tracker you own and know how to change. You will practise the whole cycle, including a real mistake the recorded build made with a broken backup file, and how one precise sentence fixed it.

Finished Lab Board with the four renamed columns Received (3), Prepared (2), Measured (2) and Reported (1). Today is set to 1 October 2026, the summary reads Showing 8 of 8 records, Overdue: 3, and EXP-102, EXP-104 and EXP-107 carry the word Overdue. (opens in a new tab)

You will finish with: a board that answers "what is waiting and what is late?" in one look, saves itself in your browser, and restores from a backup file you exported.

First watch the 2:09 result tour. It shows where you are going. The numbered steps below are the lesson: what to click in OpenCode, what to paste, and what must appear before you continue.

Silent, with on-screen explanations. OpenCode scenes are captures from the recorded build; app scenes operate the actual OpenCode-generated app. Captions and highlights were added afterwards. Read the transcript (opens in a new tab) · Download the movie (opens in a new tab).

Use: keep this manual beside OpenCode and your browser. Complete each Success check before continuing. Click any screenshot to enlarge it.

Files: reference kit: working app, three checkpoints, practice data and the exact prompts (opens in a new tab). Extract the ZIP first: Windows right-click → Extract All; Mac double-click. Keep it beside your project.

Your personal coach: Step 4 turns your OpenCode session into a one-step-at-a-time guide. For help later, paste the complete coach brief (opens in a new tab) into your project session and tell it your step number.

Three words you will meet. Record: one row of your tracker, here one experiment with an ID, title, owner, status, due date and note. Browser storage (localStorage): a small private store inside your browser where a page can keep data between visits. JSON: a plain-text file format for records; you can open it in any text editor.

Files for this journey

Download everything at once as the reference kit (ZIP) (opens in a new tab), or single files as you reach the step that needs them. Save each file into your project folder under exactly this name. If a file opens in the browser instead of downloading, press Ctrl+S (Mac: Command+S) to save it.

FileFirst used in
starter-board.json (opens in a new tab)Step 2 · Open your project
broken-board.json (opens in a new tab)Step 2 · Open your project

The kit also contains the finished app, the recorded checkpoints (checkpoints/…) and the exact prompts.

1. Get OpenCode readyLink to step

Do

  1. Open an ordinary website. Complete your university Wi-Fi sign-in if prompted.
  2. Install OpenCode Desktop: follow Step 1 of Set up OpenCode in the L1 book Start With OpenCode.
  3. Open OpenCode. For English labels, use Settings → Preferences → Language → English. In German: Einstellungen → Präferenzen → Sprache → English.

Success: OpenCode opens. Continue to the project; choose the model in Step 3.

If different: installation or Wi-Fi failure → use the seminar recovery card.

2. Open your projectLink to step

Do

  1. In Finder or File Explorer, create a folder named lab-board in Documents.
  2. Copy the data folder from the extracted reference kit into lab-board. It holds two small files: starter-board.json (opens in a new tab) (eight practice records) and broken-board.json (opens in a new tab) (the same records with one deliberate problem, for Step 13).
  3. In OpenCode, click Home → Add project.
  4. Select your lab-board folder. Click Select folder.
  5. Click New session.

An empty OpenCode session with lab-board shown below the message box and Space Bunny Free as the model inside it. (opens in a new tab)

Success: the new-session screen shows lab-board below the message box.

If different: another folder name → return Home and add the correct folder.

3. Select a working modelLink to step

Do: click the model name below the message box. Select the model assigned for the seminar. Wait until its name appears below the box.

The OpenCode model menu open below the message box, listing free models such as Space Bunny Free and the OpenAI models. The list depends on the connected account. (opens in a new tab)

Success: a model name is displayed. The next step checks that it answers.

If different: no working model or access → ask the instructor to confirm the seminar connection. This recording used Space Bunny Free; free availability is not guaranteed. If the model you need is missing from the list, click Manage models at the bottom of the menu and switch it on.

Agent control: if Plan is visible, select Build before requesting edits. In the recorded interface the agent selector is hidden and Build is the default.

4. Start your OpenCode coachLink to step

Do: copy this block, paste it into OpenCode's message box and click the upward Send arrow.

Be my patient app-building coach. I am a beginner.
We will build Lab Board in this folder, one step at a time:
a small tracker that shows which lab experiments are waiting.
Use one index.html with inline HTML, CSS and JavaScript,
no packages, server or network calls.
The data folder holds synthetic course data: starter-board.json.
Explain in at most four short lines: Do, See, Success, If different.
Wait for my result before continuing.
Never claim to see my browser unless you actually inspected it.
Do not create files yet. Reply:
"Ready. Your project is lab-board. Next: build the board."

Wait: while OpenCode works, Send turns into Stop. Continue only when Stop has disappeared.

OpenCode has answered "Ready. Your project is lab-board. Next: build the board." and waits. (opens in a new tab)

Success: OpenCode replies Ready. Your project is lab-board. No file is created yet.

If different: an access or quota error → return to Step 3. Do not reinstall OpenCode to fix a model quota.

5. Build the board: your first screenLink to step

A double-clicked page cannot open other files by itself, so the request asks OpenCode to copy the eight practice records into the app.

Do: paste this into the same session and click Send. Wait for Stop to disappear (about one minute in the recording).

Create only index.html in this project. Build the first Lab Board
screen with inline HTML, CSS and JavaScript: no packages, server or network.
Clean light design, dark teal heading "Lab Board", system fonts.
Read data/starter-board.json now and embed its exact records as the
starter board, because a double-clicked page cannot read files by itself.
Each record has id, title, owner, status, due (YYYY-MM-DD) and note.
Show four columns in this order: Planned, Running, Analysis, Done.
Each column heading shows its name and card count, e.g. "Planned (3)".
Inside a column, sort cards by due date, earliest first.
Each card shows the ID, title, owner, due date and note as text.
Add a "Load starter board" button. Before it is clicked show
"No records yet. Click Load starter board."
No moving, adding, saving or searching yet. Save index.html,
tell me how to open it, then wait.

Success: OpenCode says index.html was created with the eight starter records and tells you to double-click it.

If different: code appears only in the chat → send "Save that code as index.html in this project. Tell me the saved path." If a permission request appears, allow the file edit only when it names your project.

6. Open the app and count the columnsLink to step

Do

  1. In Finder or File Explorer, open lab-board and double-click index.html. If a text editor opens instead, right-click → Open With → your browser.
  2. The page says No records yet. Click Load starter board. Click Load starter board.

The first Lab Board: four columns Planned (3), Running (2), Analysis (2) and Done (1). Planned starts with EXP-102, due 2026-09-28. (opens in a new tab)

Success: the headings read Planned (3), Running (2), Analysis (2), Done (1). In Planned the first card is EXP-102 (due 2026-09-28), because cards are sorted by due date.

If different: a column is missing or the counts differ → send "I clicked Load starter board. I expected Planned (3), Running (2), Analysis (2), Done (1). I see [exact headings]. Fix only that."

7. Move a record forward and backLink to step

Do: send this request. Wait for Stop to disappear.

The board shows Planned (3), Running (2), Analysis (2), Done (1).
Now let me move a record through the workflow.
On every card add two real buttons: "Back to <previous status>" and
"Forward to <next status>", e.g. "Forward to Analysis".
No Back button in Planned, no Forward button in Done.
Give each button an accessible name with the record ID, e.g.
"Move EXP-104 forward to Analysis". It must work with the keyboard
alone: Tab to the button, press Enter. After a move, update the
column counts. No drag and drop.
Save index.html; tell me what to check, then wait.

After Stop disappears, switch to the browser and refresh (Ctrl+R; Mac Command+R). Click Load starter board. On card EXP-104 (Running), click Forward to Analysis.

EXP-104 has moved to Analysis: the headings now read Planned (3), Running (1), Analysis (3), Done (1). Each card has labelled Back to and Forward to buttons. (opens in a new tab)

Success: Planned (3), Running (1), Analysis (3), Done (1). Now click Back to Running on EXP-104: the counts return to 3, 2, 2, 1. Try once without the mouse: press Tab until a Forward to button is outlined, then press Enter. Planned cards have no Back button; the Done card has no Forward button.

If different: a card moves two columns or vanishes → send "I clicked Forward to Analysis on EXP-104. I expected it in Analysis with Running (1), Analysis (3). I see [result]. Fix only that."

Keep: copy the whole lab-board folder beside the original and name the copy lab-board-01-working. Continue in the original. Recorded recovery file: checkpoints/06-move/index.html in the kit.

8. Add a recordLink to step

Do: send:

Moving works: EXP-104 forward gives Planned (3), Running (1),
Analysis (3), Done (1).
Add a form "Add a record" above the board with labelled fields:
ID, Title, Owner, Status (the four statuses, default Planned),
Due date (a date picker) and Note (optional).
ID, Title, Owner and Due date are required. Trim spaces.
An ID that is already on the board is refused with a visible
message, e.g. "ID EXP-101 already exists. Use a new ID."
Show every problem in plain words next to the form; add nothing then.
After a successful add, show "Added EXP-109." (with the real ID)
and clear the form. Keep the starter board, moving and counts.
No saving yet. Save index.html; tell me what to check, then wait.

Refresh, click Load starter board, then fill the new form: ID EXP-101, Title Freezer inventory check, Owner Jules, Due date 10 October 2026. Click Add record.

The Add a record form with ID EXP-101 and the message ID EXP-101 already exists. Use a new ID. (opens in a new tab)

Success: the message reads ID EXP-101 already exists. Use a new ID. Nothing is added. Change the ID to EXP-109 and click Add record again: Added EXP-109. appears and Planned shows (4).

If different: a second EXP-101 card appears → send "I added ID EXP-101 although it is already on the board, and now there are two EXP-101 cards. Refuse an existing ID with a visible message. Fix only that."

9. Keep your work in the browserLink to step

Do: refresh the browser once more. EXP-109 is gone and Planned shows (3) again. Nothing is saved yet: the app only kept your records while the page was open. Send:

Adding works, but when I refresh the page my new record is gone.
Save the board automatically in this browser (localStorage) after
every add or move, and restore it when the page opens.
Load starter board must now ask for confirmation first, saying how
many current records it will replace. Cancel changes nothing.
If saving fails, show a visible error instead of pretending it worked.
Keep everything else. Save index.html; tell me what to check, then wait.

Refresh. Click Load starter board. The browser asks Replace 0 current records with the starter board? Click OK. Add EXP-109 again (same values as Step 8). Then refresh.

Success: after the refresh, EXP-109 is still on the board and Planned shows (4). Close the browser tab, double-click index.html again: it is still there.

If different: the record disappears after a refresh → send "I added EXP-109 and refreshed. It is gone. Save after every add or move and restore on opening. Fix only that." A private or incognito window forgets everything when you close it; use a normal window.

10. Find a record: search and ownerLink to step

Do: send:

My new record survives a refresh now.
Above the board add a "Search" text box and an "Owner" dropdown
("All" plus every owner on the board, sorted). Search matches ID,
title and note, ignoring upper and lower case. Both filters work
together. Column counts show the cards currently shown.
Show "Showing X of Y records" above the board.
When nothing matches, show "No records match. Clear the search."
Filtering never changes or saves records.
Keep everything else. Save index.html; tell me what to check, then wait.

Refresh. The line above the board reads Showing 9 of 9 records. Type blot in Search: only EXP-106 remains. Clear the search box.

Now return to the eight practice records: click Load starter board, read the question Replace 9 current records with the starter board? and click OK. Choose Jules in Owner.

Owner Jules selected: Showing 3 of 8 records, with EXP-101 in Planned (1), EXP-104 in Running (1), EXP-106 in Analysis (1) and Done (0). (opens in a new tab)

Success: Showing 3 of 8 records: EXP-101, EXP-104 and EXP-106, with Planned (1), Running (1), Analysis (1), Done (0). Typing plate with Owner All shows EXP-107 and EXP-108. Typing zzz shows No records match. Clear the search. Set Owner back to All.

If different: the counts still say 3, 2, 2, 1 while filtering → send "With Owner Jules the columns show 3 cards but the headings still say Planned (3). Count only the cards shown. Fix only that."

11. Flag overdue workLink to step

"Overdue" depends on the date. So the app gets a visible Today field: it starts at your computer's date, and you can set it to any date to check the rule.

Do: send:

Filtering works: Owner Jules shows 3 of 8 records.
Add a date field labelled "Today", defaulting to this computer's date.
A card is overdue when its due date is before Today and its status
is not Done. A card due on Today itself is not overdue.
Show the word "Overdue" on those cards (text, not colour alone), and
"Overdue: N" next to "Showing X of Y records", counting shown cards.
Changing Today only changes the flags; it never changes saved records.
Keep everything else. Save index.html; tell me what to check, then wait.

Refresh. The new Today field shows your computer's date. Click it and set it to 1 October 2026 (the field shows the date in your computer's format, for example 01.10.2026).

Today is 1 October 2026: Showing 8 of 8 records, Overdue: 3. EXP-102, EXP-104 and EXP-107 show the word Overdue; EXP-106, due on 1 October, does not. (opens in a new tab)

Success: Overdue: 3. EXP-102 (due 28 Sept), EXP-104 (due 30 Sept) and EXP-107 (due 25 Sept) say Overdue. EXP-106 is due on 1 October itself, so it is not overdue yet. EXP-108 is late on paper but Done, so it is not flagged. Choose Owner Jules: Overdue: 1 (EXP-104). Set Owner back to All and Today to 30 September 2026: Overdue drops to 2, because EXP-104 is now due today. Today is not saved: after a refresh it shows your computer's date again.

If different: EXP-106 is flagged on 1 October → send "With Today 2026-10-01, EXP-106 (due 2026-10-01) says Overdue. Due on Today is not overdue; only earlier dates are. Fix only that."

Keep: copy the project as lab-board-02-working. Recorded recovery file: checkpoints/10-overdue/index.html.

12. Back up and recoverLink to step

Browser storage lives inside one browser on one computer. Clearing browser data, switching browser or laptop, or a colleague's computer all start empty. A file you export is the backup. This step proves it.

Do: send:

With Today 2026-10-01, three cards say Overdue: EXP-102, EXP-104, EXP-107.
Add "Export JSON": it downloads lab-board-backup.json containing
{"app": "Lab Board", "version": 1, "records": [...]} with every record
(not only the filtered ones) and the same fields as starter-board.json.
Add "Import JSON", a labelled file input that reads such a file locally.
Check the file before using it: valid JSON with a records list; every
record needs id, title, owner, due (YYYY-MM-DD) and one of the four
statuses. If anything is wrong, say which record and why, and leave
the current board unchanged. A valid file replaces the whole board
after a confirmation that says how many records it replaces with how many.
Add "Clear board" with a confirmation, so I can practise recovery.
Show this sentence under these buttons, always visible:
"Browser storage is not a backup: clearing browser data, another browser
or another computer loses it. The exported file is your backup."
Keep everything else. Save index.html; tell me what to check, then wait.

Refresh. Set Owner to All if needed, then:

  1. Add EXP-109 once more (as in Step 8), so the board has 9 records.
  2. Click Export JSON. The file lab-board-backup.json appears in Downloads.
  3. Click Clear board and confirm Clear all 9 records? with OK. The board is empty.
  4. Refresh. It stays empty: the browser's copy is gone.
  5. Click the Import JSON file button (Choose File), select lab-board-backup.json from Downloads and click Open. Confirm Replace 0 records with 9 records? with OK.

The backup area: Export JSON, the Import JSON file button, a red Clear board button and the sentence Browser storage is not a backup. (opens in a new tab)

Success: all 9 records are back, including EXP-109, and Planned shows (4). The sentence Browser storage is not a backup is visible under the buttons.

If different: Export downloads only the records you filtered → send "Export JSON saved only the filtered records. Export every record on the board, whatever the filters show."

13. Test a broken file, then correct the appLink to step

A backup is only useful if the app refuses bad files. broken-board.json contains two different records that share the ID EXP-103. Two records with one ID are a classic source of mix-ups: which one did you move?

Do: click Import JSON and choose data/broken-board.json. In the recorded build the app asked Replace 9 records with 9 records? Click OK to see what it does. Your backup file is safe in Downloads.

The first build accepted the broken file: the board shows two EXP-103 cards, one in Planned and one in Running. (opens in a new tab)

The first build accepted the file. The board now shows two EXP-103 cards, and nothing warned you. The backup request had not mentioned duplicate IDs, and the app did not check them. Report exactly what you saw:

I chose data/broken-board.json in Import JSON. It asked "Replace 9 records
with 9 records?" and after OK the board shows two EXP-103 cards.
The file has two records with the ID EXP-103. IDs must be unique.
Refuse such a file before any confirmation. Name the problem, e.g.
"Not imported: ID EXP-103 appears twice (records 3 and 9)."
and leave the current board unchanged. Importing starter-board.json
must still work. Change only this. Save index.html; tell me what to
check, then wait.

OpenCode after the correction: it rejects duplicate IDs during file validation, before the confirmation, and names what to check. (opens in a new tab)

Wait for Stop to disappear. Refresh. First bring your records back: import lab-board-backup.json and click OK. Then choose data/broken-board.json again.

The corrected app refuses the file: Not imported: ID EXP-103 appears twice (records 3 and 9). The board still shows 9 of 9 records. (opens in a new tab)

Success: no confirmation appears. The message reads Not imported: ID EXP-103 appears twice (records 3 and 9). The board still shows Showing 9 of 9 records, with EXP-109.

If different: your build already refused the file → skip the correction. The message appears but the board changed → send "The duplicate message appears, but my board was replaced anyway. Leave the current board unchanged when a file is refused."

Keep: copy the project as lab-board-03-working. Recorded recovery file: checkpoints/12-dupfix/index.html.

14. Make it yoursLink to step

Your chore probably has other steps than Planned → Running → Analysis → Done. Renaming them touches your saved records too, so say what should happen to them.

Do: write down your chore's four steps in order. Replace the four names in the request below with yours (the recording used a sample queue), then send:

Make it mine. Change the heading to "My Lab Board" and keep the browser
title consistent. My chore is a sample queue, so rename the four statuses,
in order, to "Received", "Prepared", "Measured", "Reported" everywhere:
columns, buttons, the form, import checks and export.
A record that still has an old status name (saved in this browser, in the
starter board or in an imported file) gets the new name in the same position:
Planned → Received, Running → Prepared, Analysis → Measured, Done → Reported.
Do not change IDs, counts, filters, overdue rules or backup behaviour.
Save index.html and tell me what changed.

Refresh.

The same app renamed: heading My Lab Board, the Status field shows Received, and the columns read Received (3), Prepared (2), Measured (2), Reported (1) after Load starter board. (opens in a new tab)

Success: the heading reads My Lab Board. Your saved board keeps all 9 records under the new names: Received (4), Prepared (2), Measured (2), Reported (1) in the recording. Load starter board gives 3, 2, 2, 1 under the new names, and importing data/starter-board.json (old names) works too.

If different: the columns are renamed but your saved records are gone → send "After the rename my saved records disappeared. Records with an old status name must move to the new name in the same position. Nothing may be dropped." Anything else changed → restore index.html from your latest checkpoint and resend.

15. Your own permitted recordsLink to step

Do

  1. Export the practice board (Export JSON) and rename the file practice-board.json, so you can return to it.
  2. Click Clear board and confirm.
  3. Add three records from your own chore, using only information you are allowed to record: sample or experiment IDs, short titles, owners as initials or roles your team agreed on. No patient data, and no personal details about colleagues.
  4. Move one record forward. Set Today to a date where one record should be overdue, and check that it is flagged.
  5. Click Export JSON. Keep that file where your group keeps project files: it is your backup.

Privacy rule: the app sends nothing anywhere; your records stay in this browser and in the file you export. OpenCode only sees what you paste into it or ask it to read. Never paste unpublished or personal data into a model without permission. When you need help, describe the problem with a made-up record such as EXP-999, not your real one.

Success: your three records appear in the right columns, one moves forward, the overdue flag matches your own check, and your export file exists.

If different: a field your chore needs is missing (for example a freezer position) → ask OpenCode for that one field, using a made-up example value.

16. Leave a README and continue in a fresh sessionLink to step

Do: send:

Read index.html, then create README.md with exact instructions for
opening and using this app: Load starter board, add, move, Search,
Owner, Today and Overdue, Export JSON, Import JSON and Clear board.
Say where records are kept (this browser only) and that the exported
file is the backup. List the checks: the starter board shows 3, 2, 2
and 1 records per column; Owner Jules shows 3; with Today 2026-10-01
three cards are overdue. Record this next task: edit an existing record.
Do not implement it. Do not change index.html.
Tell me which file you created, then wait.

Then click New session (the + beside the session tab). Confirm lab-board is still the project. If the button says New project, click it, select your original lab-board folder and click Select folder. Send:

Read README.md and index.html without editing.
Tell me the app's purpose, how to open it and the recorded next task.
Keep the answer to three bullets.

A fresh OpenCode session answers in three bullets: an offline lab board with statuses, filtering, due-date flags, browser storage and JSON backup; open index.html by double-click and click Load starter board; next task: edit an existing record. (opens in a new tab)

Success: the new session names the app's purpose, double-clicking index.html, and edit an existing record as the next task, all from your files.

Then read README.md yourself. In the recording it said that Search and Owner "combine with Today". That is wrong: Today only sets the Overdue flags. The fix, sent in the fresh session:

In README.md the Search and Owner sections say they combine with Today.
Today is not a filter: it only sets the Overdue flags and never hides cards.
Correct only those two sentences in README.md. Do not change index.html.
Tell me the corrected sentences, then wait.

If different: empty project → check the folder. Other wrong facts → correct README.md the same way; it is your memory between sessions.

17. Let a colleague use itLink to step

Do: give a colleague index.html, README.md and data/starter-board.json, not your own records unless they may see them. Ask them to follow README.md alone: load the starter board, choose Owner Jules, set Today to 1 October 2026, and tell you the Overdue number. Then ask them to set Owner back to All and move EXP-104 forward.

Success: they report Overdue: 1 (EXP-104), and after the move the third column shows (3). Their board starts empty: every browser keeps its own board. To hand over real records, send the exported file.

If they stop: add the missing instruction to README.md. Repeat from that instruction.

18. Make your next improvementLink to step

Do: save a working checkpoint. The recorded next task is editing an existing record (fix a typo in a title or change a due date). Write your request with this pattern:

Add [one behaviour] so [my user] can [purpose].
Keep [working features].
I will know it works when [visible result].
Tell me which part will change, implement only this improvement,
then wait for me to try it.

For editing: the ID must stay unique, an edit must survive a refresh, and Cancel must change nothing.

Success: the new behaviour works, Load starter board still gives 3, 2, 2, 1, and Today 1 October 2026 still gives Overdue: 3. Export a fresh backup. Record the next task in README.md.

If stuck: paste this into your coach chat:

I am on step [number].
I did: [exact action].
I expected: [result from the manual].
I see: [actual text/result, or describe a screenshot].
Give me one next action. Wait for my result.

What this app does not do: it has no user accounts, no sharing between computers and no automatic backup. Two people cannot work on one board at the same time. It is a teaching tracker for one person in one browser, not a validated laboratory information system (LIMS).

The method behind these steps

Structure it: one record, four statuses, one rule for "overdue". Understand it: know where the data lives: browser storage for convenience, an exported file for safety. Choose tools: one offline HTML file, OpenCode as the builder, known counts as the judge. Build it: one request, one check, one checkpoint at a time, with corrections phrased as observations. Read T01-L01 · From Idea to Working Result: The Four-Quadrant Method.