Chapter 2 — Walkthrough: Recently Viewed Products
Follow the full OpenSpec loop — propose → review → apply → archive — on a real feature. Every prompt is verbatim; every expected output is shown so you can verify your agent is on track.
- Reading time: 15 minutes
- Practical time: 45–60 minutes
Feature brief
What we're building: A "Recently Viewed" strip on the product catalog page (products/overview) that shows the last 10 products the user clicked, persisted in localStorage. Clicking an item in the strip navigates back to that product's detail page.
Why this feature: It is entirely frontend — no new API endpoints, no Spring changes, no database migrations. The OpenSpec loop is front-and-centre; the Angular/localStorage mechanics are secondary. A perfect first spec to write and review.
Scope:
- New
RecentlyViewedComponentrendered below the product grid inProductCatalogPageComponent. - On every product-detail page visit, write the product id + name + image URL to a
recently-viewedkey inlocalStorage(cap at 10 items; newest first). - Strip is hidden when the list is empty.
- No login required;
localStorageis per-browser.
Step 1 — Propose
Open a new AI agent session (Copilot CLI or Claude Code) inside ~/lab-workspace/fullstack-onlineshop and run:
/opsx:propose add a recently viewed products strip to the product catalog pageThe agent reads the OpenSpec instructions injected by openspec init and generates the proposal folder. This takes 30–90 seconds depending on the model.
Expected:
openspec/
└── changes/
└── add-recently-viewed-products-strip-to-the-product-catalog-page/
├── proposal.md
├── specs/
│ └── recently-viewed.md
├── design.md
└── tasks.mdNote — folder name may vary: OpenSpec slugifies your prompt, and the agent may drop articles or shorten words. Use
ls openspec/changes/to confirm the actual folder name rather than assuming it matches the example above exactly.
Troubleshooting — CLI not found: If the agent responds "I don't recognise /opsx:propose", OpenSpec is not wired up. Run
openspec updatein the terminal and start a fresh agent session.
Step 2 — Read the proposal
Read each generated file before approving anything:
proposal.md— Does the "why" match your intent? Does the scope paragraph match the feature brief above?specs/recently-viewed.md(or similar) — Are the acceptance scenarios concrete? Do they cover: adding on detail-page visit, capping at 10, empty-state hiding, and navigation on click?design.md— Does it name the right component (ProductCatalogPageComponent) and the right storage mechanism (localStorage)? Does it avoid introducing a backend call?tasks.md— Is the checklist ordered? Could a developer follow it without reading any other file?
Take notes as you read. You will use them in Step 3.
Troubleshooting — proposal too vague: If
proposal.mdis one paragraph andtasks.mdhas two items, the agent did not fully engage with the OpenSpec format. Prompt: "The proposal is too brief. Please re-read the OpenSpec instructions and expand proposal.md, specs/, design.md, and tasks.md with full detail."
Step 3 — Refine via dialogue
Based on your Step 2 notes, give the agent revision instructions in plain English. You do not need to edit the files yourself — the agent updates them.
Here is a worked example of two common refinements:
Increase the cap from 5 to 10:
The proposal caps recently viewed at 5 items. Change it to 10 in proposal.md,
specs/recently-viewed.md, and tasks.md — wherever the cap is mentioned.Expected:
- proposal.md updated: "...stores the last 10 products..."
- specs/recently-viewed.md updated: acceptance scenario now reads "...no more than 10 entries..."
- tasks.md updated: implementation note references 10Add a remove ("×") button per item:
Add an "×" button to each item in the strip so the user can remove it from
the recently-viewed list without refreshing the page. Update specs/recently-viewed.md
with a new acceptance scenario and add a task to tasks.md.Expected:
- specs/recently-viewed.md gains a new scenario: "Given the strip is visible,
when I click × on an item, that item is removed from the strip and from localStorage."
- tasks.md gains a task: "Add click handler on × button that splices the item
from the stored array and triggers re-render."After each round of revisions, re-read the updated files. Repeat this dialogue until you are satisfied with the spec. Do not move to Step 4 until you would be comfortable handing tasks.md to a colleague as the complete implementation brief.
Troubleshooting — agent skips a step: If the agent jumps straight to writing TypeScript after
/opsx:propose, it may have bypassed the spec stage. Prompt: "Stop — do not write any TypeScript yet. We are still in the proposal stage. Please only update the Markdown files in openspec/changes/."
Step 4 — Apply
When the spec is approved, apply it:
/opsx:applyThe agent reads tasks.md and implements each item in order, writing TypeScript/HTML/CSS as needed. The agent narrates its progress in its own style — you will see it describe each task as it works through them (e.g. "Now implementing task 1 — creating the service…"). There is no fixed output format to match against. What matters is that the agent works through all items in tasks.md and finishes without errors.
After apply finishes, start the dev server (if it is not already running) and confirm the build is clean. The fullstack-onlineshop workspace contains two top-level directories: onlineshopui/ (Angular frontend) and onlineshopapi/ (Spring Boot backend). Navigate into the frontend directory:
cd onlineshopui
npm install # in case any new package was added (unlikely for this feature)
ng serve # starts the dev server on http://localhost:4200 and reports any TypeScript errorsLeave ng serve running in this terminal — you need it for Step 5.
Step 5 — Verify
With the Angular dev server running, open http://localhost:4200:
- Browse to any product detail page. Note the product name.
- Navigate back to
products/overview. - Confirm the "Recently Viewed" strip appears below the product grid, showing the product you just visited.
- Visit two more product detail pages, then return to the catalog. All three should appear, newest first.
- Click an item in the strip — it should navigate to that product's detail page.
- (If you added the "×" button in Step 3) Click "×" on an item — it should disappear from the strip without a page reload.
- To verify the 10-item cap: visit at least 12 distinct product detail pages in total, then return to
products/overview. Confirm the strip shows exactly 10 items, not 12. The oldest two should have been dropped. - Open DevTools → Application → Local Storage →
http://localhost:4200. Confirm therecently-viewedkey holds an array of exactly 10 entries.
Tip (expanded profile): If you activated the expanded profile in Chapter 1, you can run
/opsx:verifyin a new agent session as a structured alternative to the manual checklist above. The command reads the spec's acceptance criteria and drives verification automatically.
If any of these checks fail, describe the failure to the agent:
The recently-viewed strip does not appear after visiting a product detail page.
The localStorage key is being written (I can see it in DevTools), but the
component does not update. Please check the change-detection strategy and
fix it without altering the proposal files.Step 6 — Archive
Once all verification checks pass, archive the change:
/opsx:archiveExpected:
openspec/changes/add-recently-viewed-products-strip-to-the-product-catalog-page/
moved to:
openspec/changes/archive/YYYYMMDD-add-recently-viewed-products-strip-to-the-product-catalog-page/Note:
YYYYMMDDis replaced by today's date inYYYYMMDDformat (e.g.20260518-add-recently-viewed-...).
The archive folder is permanent record — future you (or a teammate) can read it to understand why this component exists, what alternatives were considered, and what the acceptance criteria were.
Recap
You just completed one full OpenSpec loop:
| Step | What happened |
|---|---|
| Propose | Agent generated proposal.md, specs/, design.md, tasks.md from a one-line idea |
| Review | You read the spec and caught vague wording before any code was written |
| Refine | You updated the cap and added a "×" button via dialogue — spec stayed the source of truth (part of the Review stage: loop back until the spec is approved) |
| Apply | Agent implemented exactly what tasks.md described |
| Verify | You confirmed the feature works in the browser (part of the Apply stage: check against acceptance criteria) |
| Archive | Completed change is preserved for audit |
Reference asset: If you want to compare your generated spec against a known-good example, see the reference folder (note: the reference uses a shortened folder name add-recently-viewed/ for readability — your workspace folder will be named add-recently-viewed-products-strip-to-the-product-catalog-page/):
assets/02-walkthrough-recently-viewed/add-recently-viewed/proposal.mdassets/02-walkthrough-recently-viewed/add-recently-viewed/design.mdassets/02-walkthrough-recently-viewed/add-recently-viewed/tasks.mdassets/02-walkthrough-recently-viewed/add-recently-viewed/specs/recently-viewed.md
These files represent what a thorough OpenSpec proposal looks like after one round of refinement. Your agent's output will differ in wording; what matters is that the structure and level of detail are comparable.
Next
Head to Chapter 3 — Apply on Your Own: Product Wishlist to run the loop independently on a more complex fullstack feature.