AI System Analyst: Design Through Specification
“I want a blog on Astro.” How many times have you handed AI that phrasing — and gotten back code that rewrites half the project every two days?
The problem isn’t the model. The problem is that between “I want a blog” and the code, there is no contract. AI guesses: stack, deploy, i18n, SEO, legal. And it guesses badly.
I closed that gap with two documents. The first is a specification for the AI analyst itself: how it should run the interview, what to cover, what to avoid. The second is the output project specification: what exactly we are building, with MUST / MUST NOT, unique IDs, and testable conditions. Both documents are live, sit in the repository, and are enforced on every agent run.
This article walks through the key fragments of both, with a breakdown of the why. It is a pure how-to: take the templates, adapt them to your context.
Why a prompt is a bad contract
A prompt is a farewell letter. “Build me a blog, I need dark design and SEO.” The model makes a pile of hidden assumptions: it grabs a starter template instead of your config, bolts on Vercel instead of S3, forgets llms.txt, treats the cookie banner as “nice to have”. Halfway through, the project turns into a tangle of compromises you never chose. Rework costs more than restarting.
A specification is a contract. When it says “MUST deploy to Yandex S3 + CDN, MUST NOT use server-side rendering, NFR-COST-001: $0/month infrastructure” — the model has no room to fantasize. It does exactly what is written, and you can verify it.
By 2026 this approach has crystallized into a methodology called Spec-Driven Development (SDD): the spec precedes the code, the code is a disposable artifact, the spec is the source of truth. GitHub Spec Kit, AWS Kiro, OpenSpec, BMAD are the main tools. Spec Kit’s loop is Constitution → Specify → Plan → Tasks → Implement; the others use slightly different phase names but the structure is the same.
Birgitta Böckeler on martinfowler.com describes a spectrum of SDD maturity: from spec-assisted (the spec helps, code is primary) through spec-anchored (the spec is the governing contract for reviews and changes) to spec-as-source (the spec is the only artifact, code is generated). The middle one is optimal for production.
But most guides skip a non-obvious step. The AI analyst itself has to be designed. This is the SDD recursion: you write a spec for the agent that will write specs for the project. Both documents below.
Document 1: the AI analyst specification
Mine is system-analyst.md — 246 lines, in ~/.config/opencode/agents/. It is simultaneously the agent’s system prompt and its SDD spec. Let’s walk through the key sections.
Identity — what the agent does and does not do
You are a senior System Analyst with 15+ years of experience in requirementsengineering, business analysis, and technical planning. You are proficient inIREB, IEEE 830, ISO/IEC/IEEE 29148:2018, and Agile methodologies.
Your job is to help the user turn vague product ideas into rigorous,implementation-ready technical plans. You do NOT write production code —you think, research, interview, and document.Why. The first thing a spec for an agent needs is a hard boundary on its scope of responsibility. “Thinks, researches, interviews, documents” — and an explicit prohibition on writing production code. Without it, the analyst slides into “let me help write this one function” and loses position. The same string later traces into the Constraints section below.
The iron laws of interviewing
### Iron Laws
1. ONE question at a time — always use the `question` tool. Presenting multiple questions simultaneously overwhelms and produces ambiguous answers.2. Adaptive pacing — adjust depth based on stakeholder engagement and clarity.3. Confirm before proceeding — every 5 turns, summarize understanding and confirm.4. Never skip to solutions — elicit requirements first, document, then propose options.Why. Breaking any of the four turns the interview into empty chatter. The most common violation is “one question at a time”: the model wants to dump a wall of five questions, the user answers the first, the rest are ignored. The second most common is skipping to solutions: “how do we implement i18n?” instead of “which languages do you need and what’s the URL structure?” The first question imposes a solution, the second elicits a requirement.
Law #3 (confirm every 5 turns) catches misunderstandings before they leak into the spec. Law #2 is the escape hatch: if the topic is clear, you do not need to roll out all 25 questions.
The eight non-functional requirement categories
Cover ALL eight NFR categories (in order — skip only if already answered):
1. Performance: "How responsive does this need to be?"2. Security: "What security requirements apply? Sensitive data? Regulations?"3. Reliability: "How critical is uptime? Acceptable downtime?"4. Scalability: "How much growth do you expect? Usage spikes?"5. Usability: "Who are the users? Technical skill level?"6. Maintainability: "Who will maintain this? How often do you expect changes?"7. Compliance: "Regulatory requirements? Industry standards?"8. Integration: "What systems does this connect to? API requirements?"Why. Juniors lose half the requirements on NFRs. “Fast”, “reliable”, “secure” are not requirements, they are signals. The eight categories force the analyst to walk each one as a separate question, not bundle “what about security and performance”. Those are different domains with different trade-offs. Each category then becomes its own section in the output spec.
Response-adaptive pathways: what to do with vague words
| Trigger | Follow-up || --------------------------- | ---------------------------------------------------------------------------------------- || "Fast" / "Quick" | "What response time are you expecting? A specific number?" || "Easy to use" / "Intuitive" | "Think of a tool you find easy to use. What makes it easy?" || "Secure" | "What specific security concerns? Data protection, access control, threat prevention?" || "Scalable" | "How much growth? From X to Y users? Usage spikes?" || "Like [competitor]" | "What specifically about [competitor] do you want to replicate? What would you improve?" |Why. Stakeholders almost always speak in abstractions. The trigger table is a drill-down script: hear a trigger → ask the concrete follow-up. Without it, the analyst accepts “fast” as a requirement and writes “fast” into the spec. With it, the analyst extracts a number.
Constraints — what turns a document into a spec
## Constraints (6 key ones out of 12)
- Do NOT write production application code. Your output is documentation and analysis.- Do NOT skip the interview process and jump to conclusions — ask first.- Do NOT assume technical decisions — propose options and let the user choose.- Do NOT ask more than one question at a time.- Each requirement must be uniquely identified, testable, and traceable to a business objective. Use FR/NFR/US/AC/EC IDs.- Use RFC 2119 keywords in specs: MUST (absolute), SHOULD (recommended), MUST NOT (prohibition), MAY (optional).Why. This is the most important section. Notice the two layers:
- Behavioral prohibitions —
Do NOT ask more than one question at a timeturns an Iron Law from a wish into a contract. If the agent breaks it, you see it immediately. - Structural rules for the output document — unique IDs, RFC 2119, traceability to a business goal. This is the line between a “farewell letter” and a “spec”. Without IDs you cannot trace; without
MUST/MUST NOTyou cannot verify; without traceability the requirement floats.
RFC 2119 is an IETF standard adopted by most mainstream SDD tools. MUST = absolute requirement, MUST NOT = explicit prohibition, SHOULD = recommendation, MAY = optional. This vocabulary makes the spec machine-readable: both human and AI see where a rule is hard and where it is a preference.
Document 2: the output project specification
Once the analyst has run against its own spec, the output is the project specification. I’ll show it on a real example — the spec for this blog, nikonov-dev.online. It was born out of a 15-question interview, contains 80 FR, 26 NFR, 22 edge cases. Here are the key fragments.
NFRs with numbers, not words
## NFR: Performance
| ID | Requirement | Target || ------------ | -------------------------------- | ---------------------------------------- || NFR-PERF-001 | Lighthouse Performance score | ≥ 99 (desktop), ≥ 90 (mobile) || NFR-PERF-005 | TTFB via CDN | < 100ms (Russia) || NFR-PERF-006 | FCP (First Contentful Paint) | < 0.5s (desktop) || NFR-PERF-007 | Build time (`astro build`) | < 60s for 100 articles || NFR-PERF-008 | JS size on article page | < 50KB (without Sandpack), Sandpack lazy || NFR-PERF-009 | Transition time between articles | < 50ms (View Transitions) |## NFR: Cost
| ID | Requirement || ------------ | ---------------------------------------------------------------------------- || NFR-COST-001 | Infrastructure: $0/month (free tier: 1 GB storage, 100K GET, 100 GB traffic) || NFR-COST-002 | CI/CD: SourceCraft (already paid) |Why. “The site should be fast” is not a requirement. NFR-PERF-001: Lighthouse ≥ 99 (desktop) is a requirement. Without a number, the AI coder picks an arbitrary threshold and is formally correct. With a number, it picks a concrete implementation: static files on S3+CDN, content-hash in bundle names, lazy-loading for heavy components.
Note NFR-COST-001: $0/month. Not “cheap” — a specific free tier with specific limits. When the AI coder sees this, it does not propose Vercel Pro — it goes straight to S3.
Every NFR has a unique ID. Six months from now, when you want to loosen a performance threshold, you change NFR-PERF-008 and trace by ID to every spot in code and tests that references it. Without IDs that’s manual archaeology.
Edge cases — what breaks in non-standard situations
| ID | Scenario | Expected behavior || ------ | ---------------------------------------------- | -------------------------------------------- || EC-006 | Article slug changed | 301 redirect (file `_redirects` or S3 rules) || EC-011 | EN version of an article missing | 404 with a suggestion to switch to RU || EC-014 | Cookies declined | Site works without Yandex.Metrika || EC-015 | RKN request on personal data | Response within 30 days (152-FZ) || EC-017 | Missing key in i18n dictionary | TypeScript compile error || EC-022 | Lighthouse on an article with Sandpack (>50KB) | NFR-PERF-008 permits it; ≥99 threshold holds |Why. Edge cases are where the spec earns its money. The bulk of rework happens not on the happy path, but in non-standard situations the AI didn’t know about. The spec forces the analyst (and you) to think them through up front.
EC-017 is my favorite: a missing key in the i18n dictionary should be a compile error, not a runtime warning. That decision lives in the spec, not on code review. EC-022 is conflict resolution: Sandpack pulls >50KB of JS, but NFR-PERF-008 permits it; the overall Lighthouse ≥99 threshold still holds. The conflict is recorded, the resolution is explicit.
Key design decisions — explicit, not implied
1. Static, not server. Astro SSG → S3 + CDN. Zero server costs.2. MDX in code, not in a CMS. Articles live in the repo, version-controlled.3. Content-hash in bundle names. CSS/JS cached for a year (immutable), HTML — 5 minutes.4. i18n via TypeScript dictionaries. UIStrings interface → missing key = compile error.5. GEO: dual optimization. llms.txt for AI crawlers + Schema.org for search engines.Selection from 8 design decisions in the spec; numbering preserved.
Why. This is the Constraints block of the output spec — decisions the AI must not re-litigate. “Static, not server” is MUST NOT server-side rendering. “MDX in code, not CMS” is MUST store articles in the repo. Each decision is made once in the spec, not rediscovered on every task.
The recursion: a spec that produces specs
The two-document coupling looks like this:
spec for the analyst (system-analyst.md) │ ▼ interview, 15–25 questions │ ▼spec for the project (nikonov-dev-blog-spec.md) │ ▼ AI coder implements against the specThe analyst runs the interview by its Iron Laws, covers the eight NFR categories, drill-downs vague words via the trigger table. The output is a project specification with MUST / MUST NOT, IDs, and edge cases. That spec is fed to the AI coder, which implements it task by task without your clarifications in the loop.
The method works the same across scales. On one end — this blog (80 FR, 26 NFR, 22 edge cases). On the other — a 10-microservice EdTech platform (200+ FR, 50+ NFR, 40+ edge cases) and an autonomous development orchestrator, AutoDev V3 (36 FR, 20 NFR). The “interview → spec → coder” cycle is identical; only the number of questions and sessions changes.
Here’s the key part: the analyst spec is not the magic of a specific model. It is a plain Markdown file. You don’t have to bake it into the system prompt. For agents with filesystem access (OpenCode, Claude Code, Cursor) — drop it next to the project and reference it from the task; the AI opens and reads it when it needs to. For chat-only bots without file access (ChatGPT, Claude.ai) — paste the load-bearing parts directly into the prompt. You switch tools, the spec stays.
What turns a document into a spec
A checklist. If your document lacks these four things, it is not a spec, it is a farewell letter.
- Unique IDs. Every requirement has an ID (
FR-PUB-001,NFR-PERF-008,EC-011). Code, tests, and commits trace by ID. A requirement changes → you find the affected code in seconds. - RFC 2119.
MUST(absolute),SHOULD(recommendation),MUST NOT(prohibition),MAY(optional). The vocabulary makes the spec machine-readable: human and AI both see how hard a rule is. - Testable conditions. An acceptance criterion must be a test: “Lighthouse ≥ 99”, “TTFB < 100ms”, “$0/month”. Not “fast”, not “cheap”, not “user-friendly”.
- Out of scope. What we are NOT doing. Without this, scope creep is inevitable — the AI adds “useful” features you never asked for.
The trigger rule for yourself: write a spec when it would anger you if AI interpreted the requirement differently. Skip the spec when you can fix it with one follow-up prompt.
What you get on the way out
After interview + spec, you have a document that:
- Feeds the AI coder without loss. The coder reads the spec, splits it into 15-minute tasks, implements the feature, and writes tests for it right away — acceptance criteria from the spec become test cases. No clarifications needed mid-flight.
- Acts as the validation gate. When the coder says “done”, you check against the spec, not against memories of a conversation.
- Survives session amnesia. If a session dies, the new one starts from the spec, not from scratch.
- Traces.
NFR-PERF-008changes → by ID you find code and tests → you edit. Minutes instead of hours of archaeology.
Minimal analyst template
The fragments above are from my live agent, with OpenCode-specific bits. Below is a stripped template that works with any AI tool. Copy it, fill the placeholders in the “Project context” block, run it.
# System Analyst Agent
You are a senior system analyst. Your job: turn vague product ideas intorigorous, implementation-ready specifications. You do NOT write productioncode — you interview, research, and document.
## Interview protocol
### Iron Laws
1. ONE question at a time. Never dump multiple questions in one turn.2. Adaptive pacing — if the topic is clear, move on; if vague, drill down.3. Every 5 turns, summarize what you've captured and confirm with the user.4. Never skip to solutions — elicit requirements first, then propose options.
### Coverage (walk in this order)
1. Stakeholders & usage scenario2. Functional requirements — core features, user workflows3. Edge cases — "what happens when X fails / is missing / takes an extreme value?"4. Non-functional requirements — cover ALL eight categories: Performance, Security, Reliability, Scalability, Usability, Maintainability, Compliance, Integration5. Data model hints — key entities, relationships6. UX/style preferences
### Drill-down on vague words
| Trigger ("fast", "secure", "scalable"...) | Follow up with a specific question || ----------------------------------------- | ------------------------------------------------- || Speed claim | "What response time, in numbers?" || Security claim | "Which specific concerns: data, access, threats?" || Growth claim | "From X to Y users? Spikes?" || "Like [competitor]" | "What exactly to replicate? What to improve?" |
### Anti-patterns (avoid)
| Don't ask | Ask instead || ------------------------------------------------- | ---------------------------- || "Don't you think X is important?" (leading) | "How important is X to you?" || "How would you implement X?" (premature solution) | "What should X accomplish?" |
## Output format (write after the interview)
A specification document with:
- **User Stories** (US-1, US-2...) — each with testable acceptance criteria- **Functional Requirements** (FR-XXX-001) — uniquely identified- **Non-Functional Requirements** (NFR-XXX-001) — with concrete numbers- **Edge Cases** (EC-XXX-001) — non-standard scenarios + expected behavior- **Constraints** — using RFC 2119 keywords: MUST / MUST NOT / SHOULD / MAY- **Out of Scope** — what we are NOT building
XXX in the IDs is a domain prefix (PERF, SEC, COST, PUB, AUTH...). What matters is uniqueness and consistency within a project.
## Project context (fill in before first run)
- Stack: <your-stack-here>- Repository layout: <your-path-conventions>- Task tracker: <tool> or "none, write spec to a markdown file"- Regulations: <GDPR / 152-ФЗ / HIPAA / none>- Out of scope: <what the analyst must NOT do>What to customize: the Project context block at the bottom is the only stack-dependent part. Everything else (Iron Laws, coverage, drill-down, output format) is universal.
Where to put it and how to invoke
The spec is a Markdown file. Where it lives and how you reference it depends on the tool.
| Tool | Where the file lives | How to invoke |
|---|---|---|
| OpenCode | ~/.config/opencode/agents/analyst.md (with YAML frontmatter: mode, model) | /agents → pick Analyst, or mention in TUI |
| Claude Code | CLAUDE.md at project root or ~/.claude/CLAUDE.md (global) | Auto-loaded at session start |
| Cursor | .cursor/rules/analyst.mdc (project) or .cursorrules (legacy) | Auto-applied when editing matching files |
| ChatGPT Plus (GPTs) | Create a GPT → paste spec into “Instructions” | Start a chat with that GPT |
| Any chatbot (Claude.ai, ChatGPT free, etc.) | — | Paste the full spec into the first message of a new conversation |
For chat-only bots without filesystem access (the last two rows) — paste the spec into every new conversation. For agents with filesystem access (the first three) — drop it once, and it works across all sessions in that project.
Conclusion
AI does not replace the analyst. It scales the analyst — but only when the analyst itself is designed. Two documents do this: a spec for the agent (how it works) and a spec for the project (what it produces). Both are contracts with MUST / MUST NOT, IDs, and testable conditions.
The method needs no specific model or platform. Take the structure from the article above, drop in your context, load it into any AI agent. On the next feature, ask the first question “describe a typical usage scenario” — and don’t let the agent skip to solutions. In 15–25 questions you will have a document you can build on.
This article shows the load-bearing fragments of both documents. The rest of system-analyst.md is plumbing: wiki paths, SourceCraft commands, project-specific conventions. Reconstruct it from the structure shown here plus your own tool stack.
FAQ
Why write a spec for the analyst when SDD tools like GitHub Spec Kit exist?
Spec Kit gives you the Constitution → Specify → Plan → Tasks → Implement loop and templates. But it does not define how the analyst runs the interview, which categories to cover, how to drill down vague words. The analyst spec is a behavior layer on top of the SDD loop. Spec Kit is compatible: constitution = my Constraints block, specify/plan/tasks = the output spec.
Can I use plain ChatGPT instead of an agent with a spec?
You can, but the result is worse. A plain chat does not hold the “one question at a time” discipline, does not cover the eight NFR categories systematically, tends to skip to solutions. If you use ChatGPT — copy the Iron Laws and Constraints from the analyst spec in and remind it to follow them.
How is a spec different from a traditional requirements document?
A traditional PRD is often prose, without IDs, without MUST / MUST NOT, without edge cases, without out-of-scope. A spec is a structured, machine-readable contract tuned for an AI agent. It formally overlaps with IEEE 830 / ISO 29148 but is more pragmatic: less bureaucracy, more testability.
Does the method fit a small task?
For trivial edits the spec is overkill. The rule: write a spec when it would anger you if AI interpreted the requirement differently. Skip it when you can fix the result with one follow-up prompt. A single-function task is usually fine without a spec.
How long does the cycle take?
A 15–25 question interview is 30–60 minutes of dialogue. Plus 15–30 minutes for the analyst to assemble the output spec. That’s an hour and a half for a specification that saves days of rework. For large projects (a platform of 10 services) — 60+ questions and several sessions, but the scale is different too.
Where do I see examples of real specs?
The blog spec (80 FR, 26 NFR, 22 edge cases) and the AutoDev V3 spec (36 FR, 20 NFR) were born from this method. Both are live documents, production code is implemented against them. The method works not in theory but in production.