Contents
- The problem
- A little about me
- Keeping decisions outside the chat
- Why OpenSpec and not Spec Kit?
- What is OpenSpec?
- Starting from an empty repository
- What OpenSpec did not cover
- Did OpenSpec prevent mistakes?
- Cost
- What I learned
β The problem
A friend of mine is a technical superintendent. They oversee the condition and safe operation of vessels (ships): inspections of equipment, records and procedures. The schedule is hectic and unpredictable, and a problem can need attention at any time.
Finishing an inspection was not the end of the work. They still had to turn answers, notes and photographs into a formal report.
The inspection used a checklist with 400 questions. Some questions did not apply to every vessel. Others needed a note, a photograph or a follow-up. The report itself was assembled in Word.
Photographs were the painful part. Each image had to be inserted, resized and positioned. One picture could shift the surrounding text or break the next section's formatting. The more evidence the inspection produced, the more time went into formatting.
The Word document also gave no sense of progress. It did not show what was done, what remained, or where to continue after a break. My friend had to hold all of that in their head while switching between other problems.
One report took at least four hours. For someone effectively on call, that was four hours on top of an already full schedule.
So the goal was simple. Guide the superintendent through the checklist, keep each note and photograph with its answer, show progress, save locally, and resume later. And it had to work offline on a Windows laptop.
π A little about me
I started as a backend engineer, then moved into infrastructure and platform engineering. These days I work with controllers and service brokers more than REST APIs. Building a desktop app on my own felt well outside my experience. I still struggle to centre a div.
GenAI changed what I was willing to try. Within weeks I had a working offline Windows app. Wails and Go handled the desktop shell and its logic, Svelte the screens, and SQLite stored inspections on the laptop. It let my friend pick a vessel, work through the questions, attach notes and photographs, stop and resume, review what was missing, and export a PDF. They produced a real report in under two hours.
The interesting part was how I got there. I did not vibe-code my way to an app.
π Keeping decisions outside the chat
This was not my day job. I worked on it when I had time, left it, and came back later.
If the plan lived only in a chat, I would have to reconstruct where I stopped and why I made each decision. A different agent in the next session would face the same problem.
So I made one rule early: the plans and decisions live next to the code, not in old conversations. A written specification became the handoff. That handoff is what let me pause the project for weeks, switch between agents and models, and can pick up from where I left.
The question was which specification workflow I could keep using without it becoming another burden.
βοΈ Why OpenSpec and not Spec Kit?
Spec Kit and OpenSpec both turn a software idea into written requirements and tasks for humans and agents.
I tried Spec Kit at work during a training exercise. I understood the problem it solves: when an agent builds software, a prompt is not enough. But the workflow felt cumbersome. It generated many files, and I grew tired of reading through them. That was my experience at the time; Spec Kit may have changed since.
OpenSpec was easier for me to follow. Each piece of work kept its reason, design, requirements and tasks together. Completed work moved into a permanent history without losing its decisions. I wanted to know whether that could carry a whole application built from nothing.
β What is OpenSpec?
OpenSpec organises work as changes.
A change is one piece of work, such as letting an unfinished inspection reopen after the app restarts. Each part has a job:
-
proposal.mdexplains why the change is needed and what is in or out of scope. -
design.mdrecords technical decisions, alternatives and risks. -
spec.mddescribes what the user or system must be able to do. -
tasks.mdsplits the implementation into work that can be checked.
I drove it from my coding-agent with a handful of commands.
The OpenSpec Workflow
/opsx-explore to discuss the problem and possible approaches./opsx-propose to generate the proposal, design, specs and tasks./opsx-apply to work through the tasks./opsx-sync to copy accepted requirements into the project's permanent specs./opsx-archive to move a completed change into history while keeping it for reference.
The agents wrote the plans, code and tests. My job was to bring the real reporting problem into the discussion, choose the scope, challenge the design, and decide whether the result was good enough. For every change I reviewed the plan before implementation and tested the result before calling it done.
π Starting from an empty repository
OpenSpec is built around changing a system that already exists, often called brownfield work, where every change edits something already there. I had the opposite: an empty folder and no application to change. So I needed a way to give the empty project direction.
I started with an umbrella OpenSpec change called guided-marpol-inspection-mvp. It captured the same goal as a roadmap: the smallest version of the product that could solve the reporting problem with one inspection type: MARPOL. Beyond what the app had to do, it named the steps that turn a finished inspection into a signed report:
- decide which checklist questions apply to a vessel;
- review findings and missing information;
- sign off; and
- export the saved inspection as a report.
The umbrella was a roadmap that outlined the changes. One of the roadmap tasks, for example, said:
Create child change
initialize-desktop-app-foundation.
Applying this task did not implement the desktop application. It created another OpenSpec change under openspec/changes/initialize-desktop-app-foundation/.
That child change had its own proposal.md, tasks.md, specifications under specs/, and a design.md when design decisions were needed. I could review those artifacts and then apply the child change to produce the application code and tests.
The umbrella change described what the complete MVP needed. Each child change described how one part would be implemented and verified.
The roadmap held around a dozen child changes.
The full MVP roadmap (all child changes)
initialize-desktop-app-foundation created the Wails, Svelte, Go, and SQLite project.prepare-verified-marpol-docx-template prepared the official Word report as an export template.create-resumable-inspection-draft created and reopened locally saved inspections.capture-vessel-profile-applicability used vessel equipment to decide which checklist sections applied.answer-guided-question-slice added the first questions, answers, notes, autosave, and progress tracking.attach-evidence-to-question connected photographs to exact checklist questions.export-docx-answer-slice placed one saved answer into the Word template.export-docx-evidence-slice placed one attached photograph into the Word template.expand-marpol-checklist-coverage extended the first working slice to the remaining MARPOL checklist.review-findings-and-actions covered possible findings and their actions.final-review-readiness checked for unanswered questions, missing evidence, and pending findings.complete-docx-report-export produced the full Word report from the inspection data saved in SQLite.package-macos-desktop-build and package-windows-desktop-build prepared installable builds for macOS and Windows.
π― Applying one roadmap task
By default, the OpenSpec /opsx-apply command worked through all the remaining tasks in a change.
If I applied the umbrella change normally, it would create all the child changes in one go. That would add detailed requirements and design decisions for later features before I had implemented the earlier ones.
I could write a natural-language instruction asking /opsx-apply to implement only one section or task. But then I had to be sure that the model interpreted the instruction in the same way that I intended.
I wanted the selection to be explicit, so I extended the command with three selectors:
-
--sectionto apply one section; -
--tasksto apply a chosen set of tasks; and -
--taskto apply one task.
This gave me a more controlled sequence.
My one task at a time loop
πΊοΈ The roadmap changed as I learned
The roadmap gave me direction, but it was not a fixed implementation contract. Its details changed as the application became real.
The original export plan was DOCX. Then my friend told me a PDF report was perfectly acceptable, so the DOCX export slices were removed and replaced by complete-pdf-report-export. Other changes appeared too: refine-vessel-and-draft-start-flow separated vessel creation from inspection creation, and the planned findings workflow was narrowed into the advisory review-and-signoff change.
A roadmap should tell us where we are going. Developing the software will always reveal things that were not visible when the first plan was written.
π The payoff: a second inspection type
Once the MARPOL workflow and PDF export were done, I archived guided-marpol-inspection-mvp and began preparing the app for a second inspection type. Changes such as multi-inspection-type-frontend removed the assumption that every inspection was MARPOL. Checklist content, applicability rules and report metadata moved into a data-driven definition, and SQLite kept only the user's work.
That is what made Fire Safety cheap. Adding it was mostly writing a new definition: its questions, its rules, its follow-up conditions. Saving, resuming, reviewing and exporting kept working through the same code. A few MARPOL assumptions were still hiding in how labels appeared, so I fixed those in the shared interface instead of building Fire-Safety-specific screens. The specs had not designed Fire Safety in advance. They had guided a sequence of simplifications that turned a second inspection type into a data change.
π§± What OpenSpec did not cover
OpenSpec described what each product change should do. It did not give every agent the same engineering and testing standards. I kept those in AGENTS.md, a file every coding agent reads.
All the tests passed, and the app still misbehaved: switching sections could open the wrong content. That pushed me to a clearer testing rule: test behaviour, not implementation. A test should check an observable result, such as what appears on screen, what SQLite saved, or what the report contained, not just whether one function called another.
I asked the agent to add end-to-end tests for the important workflows. They ran through the real layers: save an inspection to SQLite, close and reopen the app, and assert the user's work was still there. I also codified a rule in AGENTS.md: every bug fix must include a regression test that reproduces the faulty behaviour.
Changes made at different times left the interface inconsistent: buttons, font sizes and error colours varied by page. I used an OpenSpec change to define a shared design system and update the existing interface to follow it. The change also produced a design-system document under docs/, which subsequent frontend agents were required to follow so the interface did not drift again.
The split became clear: OpenSpec defined each product change, AGENTS.md held project-wide engineering rules, and specialized subagents received role-specific instructions, such as the frontend agent following the design system.
π Did OpenSpec prevent mistakes?
No.
Some bugs came from incomplete requirements. Others happened even when the design was clear. One regression appeared when I opened a question from the review screen and went back: the app showed the first section instead of the one I had last selected.
The specs reduced ambiguity. They did not remove the need to run the app, look at the output, and decide which problems needed another change.
πΈ Cost
OpenSpec sent more text to the models. Each agent had to read project instructions, specs, related code and a growing change history. Measured as input tokens, that raised cost and used up my limits. Using one strong model for everything, I hit those limits often.
So I matched the model to the task. Stronger models for design decisions and hard reviews; cheaper or free models for clear implementation and admin work like archiving. I moved between Opus 4.8, Sonnet 4.6, Haiku 4.5, GPT-5.4, GPT-5.5, DeepSeek V4 Flash and MiniMax-M3. Their styles differed, but the OpenSpec boundaries kept them aligned on the same accepted design and behaviour.
Finding context was the other cost. As the repository grew, agents searched broadly and read large parts of the project. Graphify builds a knowledge graph from the repository, connecting related code, specs and docs. An agent could ask it a focused question and get back a smaller set of connected files instead.
With task-appropriate models and Graphify, I stopped reaching my limits.
π‘ What I learned
One morning I found myself questioning an agent. Why had it ignored the instructions? Why had it left the visual guidelines? Why had it skipped a required test?
The fix was not simply a stronger model. I needed mechanical sympathy for the agent: what information helped it, where it lost direction, and when a task had grown too large. Choosing what to give it, where to record it, and when to split the work became the real skill.
This is where the specs helped most. Their value was not in predicting everything up front. They gave each agent the same reviewed starting point, kept changes small, and preserved why the design evolved. That is what let me pause the project, switch models, or revisit a feature without rebuilding the plan from memory.
I now treat the agent as a capable code-generation tool, not the person responsible for the product. I am still responsible for whether the software is useful, reliable, and maintainable. Tests, specs and agent instructions help, but they do not replace judgement.
The tools keep changing. The fundamentals keep returning in a different form.


Top comments (1)
Using a brownfield-oriented spec tool from an empty repo is a useful stress test. Greenfield work still creates decisions fast; they just become invisible sooner. Keeping decisions outside the chat is probably the biggest win, because the next session can inspect the product reasoning instead of reconstructing it.