Compare commits
59 Commits
8fc667daee
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 30c6e5bb78 | |||
| 3b4afd287e | |||
| 999acc128e | |||
| f5f03d1803 | |||
| 3c371ab88a | |||
| c7c2b17d78 | |||
| 3a093ecd6b | |||
| 5424751993 | |||
| 370fd3e67f | |||
| dbfeeee253 | |||
| 395e5fb47c | |||
| 62f3d25e81 | |||
| 5797fd0e94 | |||
| ba677a35e1 | |||
| 22b95db2f9 | |||
| 143df60485 | |||
| 656a921ff0 | |||
| 46c8e819df | |||
| 247d7d9f6e | |||
| d256f6d176 | |||
| c8052df8eb | |||
| 586a1cccd5 | |||
| b5285877bf | |||
| cbadf77517 | |||
| 6883b5b42b | |||
| 4c393bb427 | |||
| d42c92a2e1 | |||
| 7e7f1cbb67 | |||
| e134552ec5 | |||
| fcfa347005 | |||
| 619d0c5efe | |||
| 2a7a8fb49d | |||
| 629609a556 | |||
| a795ca3650 | |||
| 5c01008d8a | |||
| f6b11eb9ac | |||
| 77ce9db722 | |||
| f15a64395f | |||
| 7aa03e91fb | |||
| 3421b5f106 | |||
| 79e2d9ff92 | |||
| 6611ca5226 | |||
| f45b296b70 | |||
| 78d3ebdf11 | |||
| b5ea9050e0 | |||
| 65e2838aa9 | |||
| f443d79d9f | |||
| c64f3d8b80 | |||
| 88cc5e96ec | |||
| 8659dfc658 | |||
| 7029104e5c | |||
| 5e552d99bc | |||
| a0de08d789 | |||
| b3499adfca | |||
| cf2ff0ac1c | |||
| e2687de338 | |||
| faf973accb | |||
| 9fdfad369f | |||
| 1efb665619 |
@@ -1,30 +0,0 @@
|
||||
# Ponytail, lazy senior dev mode
|
||||
|
||||
You are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written.
|
||||
|
||||
Before writing any code, stop at the first rung that holds:
|
||||
|
||||
1. Does this need to be built at all? (YAGNI)
|
||||
2. Does it already exist in this codebase? Reuse the helper, util, or pattern that's already here, don't re-write it.
|
||||
3. Does the standard library already do this? Use it.
|
||||
4. Does a native platform feature cover it? Use it.
|
||||
5. Does an already-installed dependency solve it? Use it.
|
||||
6. Can this be one line? Make it one line.
|
||||
7. Only then: write the minimum code that works.
|
||||
|
||||
The ladder runs after you understand the problem, not instead of it: read the task and the code it touches, trace the real flow end to end, then climb.
|
||||
|
||||
Bug fix = root cause, not symptom: a report names a symptom. Grep every caller of the function you touch and fix the shared function once — one guard there is a smaller diff than one per caller, and patching only the path the ticket names leaves a sibling caller still broken.
|
||||
|
||||
Rules:
|
||||
|
||||
- No abstractions that weren't explicitly requested.
|
||||
- No new dependency if it can be avoided.
|
||||
- No boilerplate nobody asked for.
|
||||
- Deletion over addition. Boring over clever. Fewest files possible.
|
||||
- Shortest working diff wins, but only once you understand the problem. The smallest change in the wrong place isn't lazy, it's a second bug.
|
||||
- Question complex requests: "Do you actually need X, or does Y cover it?"
|
||||
- Pick the edge-case-correct option when two stdlib approaches are the same size, lazy means less code, not the flimsier algorithm.
|
||||
- Mark intentional simplifications with a `ponytail:` comment. If the shortcut has a known ceiling (global lock, O(n²) scan, naive heuristic), the comment names the ceiling and the upgrade path.
|
||||
|
||||
Not lazy about: understanding the problem (read it fully and trace the real flow before picking a rung, a small diff you don't understand is just laziness dressed up as efficiency), input validation at trust boundaries, error handling that prevents data loss, security, accessibility, the calibration real hardware needs (the platform is never the spec ideal, a clock drifts, a sensor reads off), anything explicitly requested. Lazy code without its check is unfinished: non-trivial logic leaves ONE runnable check behind, the smallest thing that fails if the logic breaks (an assert-based demo/self-check or one small test file; no frameworks, no fixtures). Trivial one-liners need no test.
|
||||
1
.agents/skills/.openspec-target
Normal file
1
.agents/skills/.openspec-target
Normal file
@@ -0,0 +1 @@
|
||||
codex
|
||||
55
.agents/skills/ask-matt/PHASE-BOUNDARIES.md
Normal file
55
.agents/skills/ask-matt/PHASE-BOUNDARIES.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Phase boundaries
|
||||
|
||||
A **phase** is a chunk of work inside a session — the grilling, the implementation, the QA. The definition is fuzzy on purpose: a phase ends when you think *"ok, we're done with that"*.
|
||||
|
||||
The **phase boundary** is the gap between two phases, and it is the only place this decision belongs. Mid-phase there is no decision to make — continue, or split the work that's left into subagents. Compacting mid-phase makes the agent lose the thread.
|
||||
|
||||
## The five options
|
||||
|
||||
| Option | What it does |
|
||||
| ------------ | --------------------------------------------------------------- |
|
||||
| **Continue** | Stay in the session. No context switch at all. |
|
||||
| **`/clear`** | Empty the context window and start from nothing. |
|
||||
| **`/handoff`** | Write a portable markdown file and seed a session anywhere with it. |
|
||||
| **Subagent** | Send the task to its own context window and get a report back. |
|
||||
| **`/compact`** | Compress this context and seed a fresh session with the summary. |
|
||||
|
||||
## The tree
|
||||
|
||||
Work top to bottom at the boundary. The first **yes** wins.
|
||||
|
||||
**1. Can you continue in this session?** Two things make the answer yes: the next phase needs this phase as a **primary source**, or you have enough [smart zone](https://www.aihero.dev/ai-coding-dictionary/smart-zone) left (~150k tokens) for the next phase to fit. Grilling → implementation is the standard yes: the implementation wants the reasoning verbatim, not a summary of it. Continue costs nothing and loses nothing, so rule it out before anything else.
|
||||
|
||||
**2. Is the context irrelevant to what comes next?** Is everything in this session — the exploration, the decisions, the dead ends — disposable? If so, **`/clear`**. It is the cheapest move on the board: it takes no time and hands back the whole window. `/clear` also isn't terminal — the old session stays resumable.
|
||||
|
||||
The cost of getting this wrong is one-way. Clear a *relevant* context and you lose the **why** behind what you built, and no amount of reading the diff back gets it returned.
|
||||
|
||||
**3. Do you need to hand off?** `/handoff` is narrow. You need it only when you are:
|
||||
|
||||
- swapping to a **new harness** (Claude → Codex),
|
||||
- moving to a **new directory** or repo,
|
||||
- sending the work to a **colleague**,
|
||||
- or forking a side task you found **mid-phase** without derailing what you're doing.
|
||||
|
||||
That list is the whole clause. What `/handoff` buys is **portability** — a file that travels. If nothing is travelling, you don't need it.
|
||||
|
||||
**4. Can the task be done AFK?** Is it scoped tightly enough to run with you away from the keyboard, no steering? Then send it to a **subagent** and leave this session untouched. Automated review is the standard case: the agent reads the diff and reports, and you aren't needed while it does.
|
||||
|
||||
**5. Otherwise, `/compact`.** Relevant context, same harness, same directory, and you need to stay in the loop — this is where the tree lands, and it lands here often. Pass it an instruction (`/compact we're going to QA this area`) so the summary keeps what the next phase needs.
|
||||
|
||||
`/compact` is the **default, not the first reach**. It sits at the bottom because the four questions above it are all cheaper or more precise. The failure mode when people start here is a fresh session that is confidently wrong about a decision the summary flattened.
|
||||
|
||||
## Primary and secondary sources
|
||||
|
||||
Every move except **Continue** turns a **primary source** into a **secondary source** — the session as it happened, replaced by a summary of it. The trade is always the same shape:
|
||||
|
||||
| Source | Information | Noise | Room to move |
|
||||
| --------------------------------- | ----------- | ----- | ------------ |
|
||||
| Primary (Continue) | Full | Lots | Little |
|
||||
| Secondary (`/compact`, `/handoff`) | Lossy | Less | Lots |
|
||||
|
||||
This is why question 1 comes first. You only pay the lossiness when staying costs more than it saves.
|
||||
|
||||
## These are judgement calls
|
||||
|
||||
The questions are not objective — each has taste in it, and the same boundary can go two ways on two days. The value is in asking them **in order**, at the boundary rather than in the middle of the work.
|
||||
@@ -14,13 +14,13 @@ A **flow** is a path through the skills. Most paths run along one **main flow**,
|
||||
|
||||
The route most work travels. You have an idea and want it built.
|
||||
|
||||
1. **`/grill-with-docs`** — sharpen the idea by interview. Start here when you **have a codebase**: it's stateful, retaining what it learns in `CONTEXT.md` and ADRs. (No codebase? Use `/grill-me` — see Standalone. Both run the same `/grilling` primitive; `grill-with-docs` is the one that leaves a paper trail.)
|
||||
2. **Branch — can you settle every question in conversation?** If a question needs a runnable answer (state, business logic, a UI you have to see), detour through a prototype, bridged by **`/handoff`** in both directions (see Crossing sessions):
|
||||
1. **`/grill-with-docs`** — sharpen the idea by interview. Start here whenever you are **working in a working directory**: it's stateful, retaining what it learns in `CONTEXT.md` and ADRs. (No working directory? Use `/grill-me` — see Standalone. Both run the same `/grilling` primitive; `grill-with-docs` is the one that leaves a paper trail, which makes it the better of the two whenever a repo is there to leave it in.)
|
||||
2. **Branch — can you settle every question in conversation?** If a question needs a runnable answer (state, business logic, a UI you have to see), detour through a prototype, bridged by **`/handoff`** in both directions (a prototype lives in its own directory, which is exactly what `/handoff` is for — see Phase boundaries):
|
||||
- **`/handoff`** out, then open a fresh session against that file,
|
||||
- **`/prototype`** to answer the question with throwaway code,
|
||||
- **`/handoff`** back what you learned, and reference it from the original idea thread.
|
||||
3. **Branch — is this a multi-session build?**
|
||||
- **Yes** → **`/to-spec`** (turn the thread into a spec), then **`/to-tickets`** to split it into tracer-bullet tickets, each declaring its **blocking edges**. On a local tracker that's one file per ticket under `.scratch/<feature>/issues/`, worked blockers-first by hand; on a real tracker the edges become native blocking links, so any ticket whose blockers are done can be grabbed — kick off **`/implement`** per ticket, **clearing context between each one**.
|
||||
- **Yes** → **`/to-spec`** (turn the thread into a spec), then **`/to-tickets`** to split it into tracer-bullet tickets, each declaring its **blocking edges**. On a local tracker that's one file per ticket under `.scratch/<feature>/issues/`, worked blockers-first by hand; on a real tracker the edges become native blocking links, so any ticket whose blockers are done can be grabbed — kick off **`/implement`** per ticket, **`/clear`ing context between each one**. Each ticket is self-contained, so the last one's context is disposable.
|
||||
- **No** → **`/implement`** right here, in the same context window.
|
||||
|
||||
Either way, **`/implement`** builds each issue by driving **`/tdd`** internally — one red-green slice at a time — then closes out by running **`/code-review`**, a two-axis review (Standards + Spec) of the diff, before committing. Reach for **`/tdd`** on its own when you just want to build a concrete behaviour test-first without a full spec, and **`/code-review`** on its own whenever you want to review a branch or PR against a fixed point.
|
||||
@@ -29,7 +29,7 @@ The route most work travels. You have an idea and want it built.
|
||||
|
||||
Keep steps 1–3 in **one unbroken context window** — don't compact or clear until after `/to-tickets` — so the grilling, spec, and tickets all build on the same thinking. Each `/implement` then starts fresh, working from the ticket.
|
||||
|
||||
The limit on this is the **[smart zone](https://www.aihero.dev/ai-coding-dictionary/smart-zone)**: the window (~120k tokens on state-of-the-art models) within which the model still reasons sharply. If a session approaches it before `/to-tickets`, don't push on degraded — `/handoff` and continue in a fresh thread.
|
||||
The limit on this is the **[smart zone](https://www.aihero.dev/ai-coding-dictionary/smart-zone)**: the window (~150k tokens on state-of-the-art models) within which the model still reasons sharply. If a session approaches it before `/to-tickets`, don't push on degraded — `/compact` at the nearest phase boundary and carry on (see Phase boundaries).
|
||||
|
||||
## On-ramps
|
||||
|
||||
@@ -58,20 +58,32 @@ Two model-invoked references that run *beneath* the other skills — each the si
|
||||
- **`/domain-modeling`** — sharpen the project's *domain* language: challenge a fuzzy term, resolve an overloaded word ("account" doing three jobs), record a hard-to-reverse decision as an ADR. It's the active discipline `/grill-with-docs` drives to keep `CONTEXT.md` a clean glossary.
|
||||
- **`/codebase-design`** — the deep-module vocabulary (module, interface, depth, seam, adapter, leverage, locality) for designing a module's *shape*: a lot of behaviour behind a small interface at a clean seam. `/tdd` and `/improve-codebase-architecture` both speak it.
|
||||
|
||||
## Crossing sessions
|
||||
## Phase boundaries
|
||||
|
||||
- **`/handoff`** — when a thread is full or you need to branch off (e.g. into a `/prototype` session), this compacts the conversation into a markdown file. You don't continue in place — you **open a new session and reference that file** to carry the context across. It's the bridge between context windows, in either direction. Use it when you want a **fresh session** but need the **current conversation preserved**.
|
||||
- **`/compact`** (built-in) — stay in the **same conversation**, letting the earlier turns be summarized. Use it at **intentional breaks between phases**, when you don't mind losing the verbatim history. Don't compact mid-phase — the agent can lose its way. `/handoff` forks; `/compact` continues.
|
||||
A **phase** is a chunk of work inside a session — the grilling, the implementation, the QA. At the **boundary** between two of them you have five options, and picking between them is the fuzziest decision in this whole map:
|
||||
|
||||
- **Continue** — stay put. Costs nothing, loses nothing.
|
||||
- **`/clear`** — empty the window, when nothing here matters to what's next.
|
||||
- **`/handoff`** — write a portable markdown file. Narrow: only for a **new harness**, a **new directory**, a **colleague**, or forking a side task **mid-phase**. What it buys is portability.
|
||||
- **Subagent** — send a tightly-scoped task to its own window and get a report back.
|
||||
- **`/compact`** — compress this context and seed a fresh session with it. The **default**, at the bottom of the tree rather than the first reach.
|
||||
|
||||
Read [PHASE-BOUNDARIES.md](PHASE-BOUNDARIES.md) for the ordered tree — the five questions, the reasoning behind each branch, and why the primary-source cost makes **Continue** the one to rule out first. Make the decision **at** a boundary; mid-phase, continue or split the rest into subagents.
|
||||
|
||||
## Standalone
|
||||
|
||||
Off the main flow entirely.
|
||||
|
||||
- **`/grill-me`** — the same relentless interview as `/grill-with-docs`, but for when you have **no codebase**. Stateless: it saves nothing locally, builds no `CONTEXT.md`. Reach for it to sharpen any plan or design that doesn't live in a repo.
|
||||
- **`/prototype`** — a small, throwaway program that answers one design question: does this state model feel right, or what should this UI look like. Throwaway from day one — keep the answer, delete the code. It's the detour in step 2 of the main flow, but reach for it any time a design question is hard to settle on paper.
|
||||
- **`/grill-me`** — the same relentless interview as `/grill-with-docs`, but **stateless**: it saves nothing locally and builds no `CONTEXT.md`. Reach for it when you are **not working in a working directory** — sharpening a plan, a design, a piece of writing, anything with no repo under it. If you are in a working directory, use `/grill-with-docs` instead: it runs the same interview and leaves a paper trail, so it is strictly the better one.
|
||||
- **`/grilling`** — the interview primitive itself: rounds, the frontier, facts are the agent's job and decisions are yours. `/grill-me` and `/grill-with-docs` are the two named ways in, and `/triage`, `/wayfinder` and `/improve-codebase-architecture` all run it internally. Reach for it directly only when you want the interview with no wrapper around it.
|
||||
- **`/resolving-merge-conflicts`** — work an in-progress merge or rebase conflict hunk by hunk, resolving by **intent** traced to each side's primary source rather than by picking lines, then finish the operation. It never runs `--abort`. Standalone and off every flow: reach for it when you are already mid-conflict.
|
||||
- **`/prototype`** — a small, throwaway program that answers one design question: does this state model feel right, or what should this UI look like. Throwaway is a constraint on how the code is written, not a promise to destroy it: the answer folds into the real code, and the prototype itself is kept as a **primary source** on a `prototype/<name>` branch out of main, pointed at from the implementation issue. It's the detour in step 2 of the main flow, but reach for it any time a design question is hard to settle on paper.
|
||||
- **`/research`** — delegate reading legwork to a **background agent**: it investigates a question against **primary sources**, then leaves a cited Markdown file in the repo. Keep working while it reads. The file it produces is something to take *into* the main flow at `/grill-with-docs` — research feeds the thinking, it doesn't replace it.
|
||||
- **`/to-questionnaire`** — when the thing blocking you isn't in your head or the codebase but in **someone else's**, this writes them a questionnaire to fill in. It's the inverse of `/grill-me`: instead of interviewing you about the subject, it interviews you about the **send** — who it's going to, what you need back — and aims the questions at the gap. What comes back is material for `/grill-with-docs` or `/to-spec`.
|
||||
- **`/wizard`** — for the steps only a **human** can take: provisioning infrastructure, setting up credentials or CI secrets, clicking through an unfamiliar third-party dashboard, running a one-off migration or cutover. It generates an interactive bash script that opens each URL, captures each value, and writes it into `.env` and GitHub secrets — so the procedure stops being something you re-explain to an agent every time. Model-invoked, so the agent reaches for it the moment it hits a wall only you can pass. If the agent could just do it itself, it should; this is for where a human is genuinely in the loop.
|
||||
- **`/wait-what`** — the corrective for a message that didn't land. Use it mid-conversation, inside any other skill, and the agent re-pitches what it just said with the context you were missing, in plain English, using the `CONTEXT.md` vocabulary. It works after the fact; `/grill-with-docs` is the upfront cure, because a shared language agreed early is what stops the jargon arriving at all.
|
||||
- **`/teach`** — learn a concept over multiple sessions, using the current directory as a stateful workspace.
|
||||
- **`/writing-great-skills`** — reference for writing and editing skills well.
|
||||
- **`/writing-for-agents`** — reference for writing documents agents consume: skills, AGENTS.md, pointed-at docs.
|
||||
|
||||
## Precondition
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
name: caveman
|
||||
description: >
|
||||
Ultra-compressed communication mode. Cuts token usage ~75% by dropping
|
||||
filler, articles, and pleasantries while keeping full technical accuracy.
|
||||
Use when user says "caveman mode", "talk like caveman", "use caveman",
|
||||
"less tokens", "be brief", or invokes /caveman.
|
||||
---
|
||||
|
||||
Respond terse like smart caveman. All technical substance stay. Only fluff die.
|
||||
|
||||
## Persistence
|
||||
|
||||
ACTIVE EVERY RESPONSE once triggered. No revert after many turns. No filler drift. Still active if unsure. Off only when user says "stop caveman" or "normal mode".
|
||||
|
||||
## Rules
|
||||
|
||||
Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Fragments OK. Short synonyms (big not extensive, fix not "implement a solution for"). Abbreviate common terms (DB/auth/config/req/res/fn/impl). Strip conjunctions. Use arrows for causality (X -> Y). One word when one word enough.
|
||||
|
||||
Technical terms stay exact. Code blocks unchanged. Errors quoted exact.
|
||||
|
||||
Pattern: `[thing] [action] [reason]. [next step].`
|
||||
|
||||
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
|
||||
Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:"
|
||||
|
||||
### Examples
|
||||
|
||||
**"Why React component re-render?"**
|
||||
|
||||
> Inline obj prop -> new ref -> re-render. `useMemo`.
|
||||
|
||||
**"Explain database connection pooling."**
|
||||
|
||||
> Pool = reuse DB conn. Skip handshake -> fast under load.
|
||||
|
||||
## Auto-Clarity Exception
|
||||
|
||||
Drop caveman temporarily for: security warnings, irreversible action confirmations, multi-step sequences where fragment order risks misread, user asks to clarify or repeats question. Resume caveman after clear part done.
|
||||
|
||||
Example -- destructive op:
|
||||
|
||||
> **Warning:** This will permanently delete all rows in the `users` table and cannot be undone.
|
||||
>
|
||||
> ```sql
|
||||
> DROP TABLE users;
|
||||
> ```
|
||||
>
|
||||
> Caveman resume. Verify backup exist first.
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
name: code-review
|
||||
description: Review the changes since a fixed point (commit, branch, tag, or merge-base) along two axes — Standards (does the code follow this repo's documented coding standards?) and Spec (does the code match what the originating issue/PRD asked for?). Runs both reviews in parallel sub-agents and reports them side by side. Use when the user wants to review a branch, a PR, work-in-progress changes, or asks to "review since X".
|
||||
description: Review the changes since a fixed point (commit, branch, tag, or merge-base) along two axes — Standards (does the code follow this repo's documented coding standards?) and Spec (does the code match what the originating issue/spec asked for?). Runs both reviews in parallel sub-agents and reports them side by side. Use when the user wants to review a branch, a PR, work-in-progress changes, or asks to "review since X".
|
||||
---
|
||||
|
||||
Two-axis review of the diff between `HEAD` and a fixed point the user supplies:
|
||||
|
||||
- **Standards** — does the code conform to this repo's documented coding standards?
|
||||
- **Spec** — does the code faithfully implement the originating issue / PRD / spec?
|
||||
- **Spec** — does the code faithfully implement the originating issue / spec?
|
||||
|
||||
Both axes run as **parallel sub-agents** so they don't pollute each other's context, then this skill aggregates their findings.
|
||||
|
||||
@@ -28,7 +28,7 @@ Look for the originating spec, in this order:
|
||||
|
||||
1. Issue references in the commit messages (`#123`, `Closes #45`, GitLab `!67`, etc.) — fetch via the workflow in `docs/agents/issue-tracker.md`.
|
||||
2. A path the user passed as an argument.
|
||||
3. A PRD/spec file under `docs/`, `specs/`, or `.scratch/` matching the branch name or feature.
|
||||
3. A spec file under `docs/`, `specs/`, or `.scratch/` matching the branch name or feature.
|
||||
4. If nothing is found, ask the user where the spec is. If they say there isn't one, the **Spec** sub-agent will skip and report "no spec available".
|
||||
|
||||
### 3. Identify the standards sources
|
||||
@@ -57,8 +57,6 @@ Each smell reads *what it is* → *how to fix*; match it against the diff:
|
||||
|
||||
### 4. Spawn both sub-agents in parallel
|
||||
|
||||
Send a single message with two `Agent` tool calls. Use the `general-purpose` subagent for both.
|
||||
|
||||
**Standards sub-agent prompt** — include:
|
||||
|
||||
- The full diff command and commit list.
|
||||
|
||||
@@ -18,7 +18,7 @@ Show this to the user, then immediately proceed to Step 2. The user reads and th
|
||||
|
||||
### 2. Spawn sub-agents
|
||||
|
||||
Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a **radically different** interface for the deepened module.
|
||||
Spawn 3+ sub-agents in parallel. Each must produce a **radically different** interface for the deepened module.
|
||||
|
||||
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:
|
||||
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
---
|
||||
name: diagnose
|
||||
description: Disciplined diagnosis loop for hard bugs and performance regressions. Reproduce → minimise → hypothesise → instrument → fix → regression-test. Use when user says "diagnose this" / "debug this", reports a bug, says something is broken/throwing/failing, or describes a performance regression.
|
||||
---
|
||||
|
||||
# Diagnose
|
||||
|
||||
A discipline for hard bugs. Skip phases only when explicitly justified.
|
||||
|
||||
When exploring the codebase, use the project's domain glossary to get a clear mental model of the relevant modules, and check ADRs in the area you're touching.
|
||||
|
||||
## Phase 1 — Build a feedback loop
|
||||
|
||||
**This is the skill.** Everything else is mechanical. If you have a fast, deterministic, agent-runnable pass/fail signal for the bug, you will find the cause — bisection, hypothesis-testing, and instrumentation all just consume that signal. If you don't have one, no amount of staring at code will save you.
|
||||
|
||||
Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.**
|
||||
|
||||
### Ways to construct one — try them in roughly this order
|
||||
|
||||
1. **Failing test** at whatever seam reaches the bug — unit, integration, e2e.
|
||||
2. **Curl / HTTP script** against a running dev server.
|
||||
3. **CLI invocation** with a fixture input, diffing stdout against a known-good snapshot.
|
||||
4. **Headless browser script** (Playwright / Puppeteer) — drives the UI, asserts on DOM/console/network.
|
||||
5. **Replay a captured trace.** Save a real network request / payload / event log to disk; replay it through the code path in isolation.
|
||||
6. **Throwaway harness.** Spin up a minimal subset of the system (one service, mocked deps) that exercises the bug code path with a single function call.
|
||||
7. **Property / fuzz loop.** If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode.
|
||||
8. **Bisection harness.** If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can `git bisect run` it.
|
||||
9. **Differential loop.** Run the same input through old-version vs new-version (or two configs) and diff outputs.
|
||||
10. **HITL bash script.** Last resort. If a human must click, drive _them_ with `scripts/hitl-loop.template.sh` so the loop is still structured. Captured output feeds back to you.
|
||||
|
||||
Build the right feedback loop, and the bug is 90% fixed.
|
||||
|
||||
### Iterate on the loop itself
|
||||
|
||||
Treat the loop as a product. Once you have _a_ loop, ask:
|
||||
|
||||
- Can I make it faster? (Cache setup, skip unrelated init, narrow the test scope.)
|
||||
- Can I make the signal sharper? (Assert on the specific symptom, not "didn't crash".)
|
||||
- Can I make it more deterministic? (Pin time, seed RNG, isolate filesystem, freeze network.)
|
||||
|
||||
A 30-second flaky loop is barely better than no loop. A 2-second deterministic loop is a debugging superpower.
|
||||
|
||||
### Non-deterministic bugs
|
||||
|
||||
The goal is not a clean repro but a **higher reproduction rate**. Loop the trigger 100×, parallelise, add stress, narrow timing windows, inject sleeps. A 50%-flake bug is debuggable; 1% is not — keep raising the rate until it's debuggable.
|
||||
|
||||
### When you genuinely cannot build a loop
|
||||
|
||||
Stop and say so explicitly. List what you tried. Ask the user for: (a) access to whatever environment reproduces it, (b) a captured artifact (HAR file, log dump, core dump, screen recording with timestamps), or (c) permission to add temporary production instrumentation. Do **not** proceed to hypothesise without a loop.
|
||||
|
||||
Do not proceed to Phase 2 until you have a loop you believe in.
|
||||
|
||||
## Phase 2 — Reproduce
|
||||
|
||||
Run the loop. Watch the bug appear.
|
||||
|
||||
Confirm:
|
||||
|
||||
- [ ] The loop produces the failure mode the **user** described — not a different failure that happens to be nearby. Wrong bug = wrong fix.
|
||||
- [ ] The failure is reproducible across multiple runs (or, for non-deterministic bugs, reproducible at a high enough rate to debug against).
|
||||
- [ ] You have captured the exact symptom (error message, wrong output, slow timing) so later phases can verify the fix actually addresses it.
|
||||
|
||||
Do not proceed until you reproduce the bug.
|
||||
|
||||
## Phase 3 — Hypothesise
|
||||
|
||||
Generate **3–5 ranked hypotheses** before testing any of them. Single-hypothesis generation anchors on the first plausible idea.
|
||||
|
||||
Each hypothesis must be **falsifiable**: state the prediction it makes.
|
||||
|
||||
> Format: "If <X> is the cause, then <changing Y> will make the bug disappear / <changing Z> will make it worse."
|
||||
|
||||
If you cannot state the prediction, the hypothesis is a vibe — discard or sharpen it.
|
||||
|
||||
**Show the ranked list to the user before testing.** They often have domain knowledge that re-ranks instantly ("we just deployed a change to #3"), or know hypotheses they've already ruled out. Cheap checkpoint, big time saver. Don't block on it — proceed with your ranking if the user is AFK.
|
||||
|
||||
## Phase 4 — Instrument
|
||||
|
||||
Each probe must map to a specific prediction from Phase 3. **Change one variable at a time.**
|
||||
|
||||
Tool preference:
|
||||
|
||||
1. **Debugger / REPL inspection** if the env supports it. One breakpoint beats ten logs.
|
||||
2. **Targeted logs** at the boundaries that distinguish hypotheses.
|
||||
3. Never "log everything and grep".
|
||||
|
||||
**Tag every debug log** with a unique prefix, e.g. `[DEBUG-a4f2]`. Cleanup at the end becomes a single grep. Untagged logs survive; tagged logs die.
|
||||
|
||||
**Perf branch.** For performance regressions, logs are usually wrong. Instead: establish a baseline measurement (timing harness, `performance.now()`, profiler, query plan), then bisect. Measure first, fix second.
|
||||
|
||||
## Phase 5 — Fix + regression test
|
||||
|
||||
Write the regression test **before the fix** — but only if there is a **correct seam** for it.
|
||||
|
||||
A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site. If the only available seam is too shallow (single-caller test when the bug needs multiple callers, unit test that can't replicate the chain that triggered the bug), a regression test there gives false confidence.
|
||||
|
||||
**If no correct seam exists, that itself is the finding.** Note it. The codebase architecture is preventing the bug from being locked down. Flag this for the next phase.
|
||||
|
||||
If a correct seam exists:
|
||||
|
||||
1. Turn the minimised repro into a failing test at that seam.
|
||||
2. Watch it fail.
|
||||
3. Apply the fix.
|
||||
4. Watch it pass.
|
||||
5. Re-run the Phase 1 feedback loop against the original (un-minimised) scenario.
|
||||
|
||||
## Phase 6 — Cleanup + post-mortem
|
||||
|
||||
Required before declaring done:
|
||||
|
||||
- [ ] Original repro no longer reproduces (re-run the Phase 1 loop)
|
||||
- [ ] Regression test passes (or absence of seam is documented)
|
||||
- [ ] All `[DEBUG-...]` instrumentation removed (`grep` the prefix)
|
||||
- [ ] Throwaway prototypes deleted (or moved to a clearly-marked debug location)
|
||||
- [ ] The hypothesis that turned out correct is stated in the commit / PR message — so the next debugger learns
|
||||
|
||||
**Then ask: what would have prevented this bug?** If the answer involves architectural change (no good test seam, tangled callers, hidden coupling) hand off to the `/improve-codebase-architecture` skill with the specifics. Make the recommendation **after** the fix is in, not before — you have more information now than when you started.
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Human-in-the-loop reproduction loop.
|
||||
# Copy this file, edit the steps below, and run it.
|
||||
# The agent runs the script; the user follows prompts in their terminal.
|
||||
#
|
||||
# Usage:
|
||||
# bash hitl-loop.template.sh
|
||||
#
|
||||
# Two helpers:
|
||||
# step "<instruction>" → show instruction, wait for Enter
|
||||
# capture VAR "<question>" → show question, read response into VAR
|
||||
#
|
||||
# At the end, captured values are printed as KEY=VALUE for the agent to parse.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
step() {
|
||||
printf '\n>>> %s\n' "$1"
|
||||
read -r -p " [Enter when done] " _
|
||||
}
|
||||
|
||||
capture() {
|
||||
local var="$1" question="$2" answer
|
||||
printf '\n>>> %s\n' "$question"
|
||||
read -r -p " > " answer
|
||||
printf -v "$var" '%s' "$answer"
|
||||
}
|
||||
|
||||
# --- edit below ---------------------------------------------------------
|
||||
|
||||
step "Open the app at http://localhost:3000 and sign in."
|
||||
|
||||
capture ERRORED "Click the 'Export' button. Did it throw an error? (y/n)"
|
||||
|
||||
capture ERROR_MSG "Paste the error message (or 'none'):"
|
||||
|
||||
# --- edit above ---------------------------------------------------------
|
||||
|
||||
printf '\n--- Captured ---\n'
|
||||
printf 'ERRORED=%s\n' "$ERRORED"
|
||||
printf 'ERROR_MSG=%s\n' "$ERROR_MSG"
|
||||
@@ -9,6 +9,12 @@ A discipline for hard bugs. Skip phases only when explicitly justified.
|
||||
|
||||
When exploring the codebase, read `CONTEXT.md` (if it exists) to get a clear mental model of the relevant modules, and check ADRs in the area you're touching.
|
||||
|
||||
## Redact
|
||||
|
||||
This skill has you show commands, outputs and captured artifacts. **Redact every secret first** — write `<REDACTED>` in its place. Build loops against env vars, so the credential stays in the environment rather than in what you show. Captured artifacts carry auth headers: quote only the lines that carry the signal.
|
||||
|
||||
If the redacted output is not enough to diagnose the bug, say so and ask the user.
|
||||
|
||||
## Phase 1 — Build a feedback loop
|
||||
|
||||
**This is the skill.** Everything else is mechanical. If you have a **tight** pass/fail signal for the bug — one that goes red on _this_ bug — you will find the cause; bisection, hypothesis-testing, and instrumentation all just consume it. If you don't have one, no amount of staring at code will save you.
|
||||
@@ -46,11 +52,11 @@ The goal is not a clean repro but a **higher reproduction rate**. Loop the trigg
|
||||
|
||||
### When you genuinely cannot build a loop
|
||||
|
||||
Stop and say so explicitly. List what you tried. Ask the user for: (a) access to whatever environment reproduces it, (b) a captured artifact (HAR file, log dump, core dump, screen recording with timestamps), or (c) permission to add temporary production instrumentation. Do **not** proceed to hypothesise without a loop.
|
||||
Stop and say so explicitly. List what you tried. Ask the user for: (a) access to whatever environment reproduces it, (b) a redacted captured artifact (HAR file, log dump, core dump, screen recording with timestamps), or (c) permission to add temporary production instrumentation. Do **not** proceed to hypothesise without a loop.
|
||||
|
||||
### Completion criterion — a tight loop that goes red
|
||||
|
||||
Phase 1 is done when the loop is **tight** and **red-capable**: you can name **one command** — a script path, a test invocation, a curl — that you have **already run at least once** (paste the invocation and its output), and that is:
|
||||
Phase 1 is done when the loop is **tight** and **red-capable**: you can name **one command** — a script path, a test invocation, a curl — that you have **already run at least once** (show the invocation and its output, redacted), and that is:
|
||||
|
||||
- [ ] **Red-capable** — it drives the actual bug code path and asserts the **user's exact symptom**, so it can go red on this bug and green once fixed. Not "runs without erroring" — it must be able to _catch this specific bug_.
|
||||
- [ ] **Deterministic** — same verdict every run (flaky bugs: a pinned, high reproduction rate, per above).
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
# capture VAR "<question>" → show question, read response into VAR
|
||||
#
|
||||
# At the end, captured values are printed as KEY=VALUE for the agent to parse.
|
||||
#
|
||||
# `capture` prints its value back to the terminal, where the agent reads it — so
|
||||
# capture observations, and leave signing in to the user as a `step`.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
@@ -3,10 +3,20 @@ name: grilling
|
||||
description: Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases.
|
||||
---
|
||||
|
||||
Interview me relentlessly about every aspect of this until we reach a shared understanding. Walk down each branch of the decision tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.
|
||||
Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: every decision branches into the decisions that hang off it.
|
||||
|
||||
Ask the questions one at a time, waiting for feedback on each question before continuing. Asking multiple questions at once is bewildering.
|
||||
Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled — the questions you can ask _now_ without guessing at answers you haven't heard yet. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait for the user's answers before the next round.
|
||||
|
||||
If a *fact* can be found by exploring the environment (filesystem, tools, etc.), look it up rather than asking me. The *decisions*, though, are mine — put each one to me and wait for my answer.
|
||||
Each question should be formatted like so:
|
||||
|
||||
Do not act on it until I confirm we have reached a shared understanding.
|
||||
```
|
||||
❓ **Q1** - **<question title>**: <question body, might be multiple paragraphs, including multiple choices>
|
||||
|
||||
➡️ <your recommended answer>
|
||||
```
|
||||
|
||||
Each round the user answers reshapes the tree — settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one.
|
||||
|
||||
Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it — don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report — ask the rest of the frontier now. The _decisions_ are the user's — put each to them and wait.
|
||||
|
||||
The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
interface:
|
||||
display_name: "Grilling"
|
||||
short_description: "Stress-test thinking one question at a time"
|
||||
short_description: "Stress-test thinking a round of questions at a time"
|
||||
|
||||
@@ -24,7 +24,7 @@ This command is _informed_ by the project's domain model and built on a shared d
|
||||
|
||||
Read the project's domain glossary (`CONTEXT.md`) and any ADRs in the area you're touching first.
|
||||
|
||||
Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction:
|
||||
Then spawn a sub-agent to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction:
|
||||
|
||||
- Where does understanding one concept require bouncing between many small modules?
|
||||
- Where are modules **shallow** — interface nearly as complex as the implementation?
|
||||
|
||||
@@ -7,14 +7,14 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Implement tasks from an OpenSpec change.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
**Input**: Optionally specify a change name (e.g., `$openspec-apply-change (Codex) or /openspec-apply-change (other agents) add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
@@ -23,9 +23,9 @@ Implement tasks from an OpenSpec change.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:apply <other>`).
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `$openspec-apply-change (Codex) or /openspec-apply-change (other agents) <other>`).
|
||||
|
||||
2. **Check status to understand the schema**
|
||||
```bash
|
||||
@@ -47,12 +47,29 @@ Implement tasks from an OpenSpec change.
|
||||
- Progress (total, complete, remaining)
|
||||
- Task list with status
|
||||
- Dynamic instruction based on current state
|
||||
- Optional `context`: current required project instruction input from the selected root
|
||||
- Optional `operationGuidance`: current advisory guidance for apply
|
||||
|
||||
**Handle states:**
|
||||
- If `state: "blocked"` (missing artifacts): show message, suggest using openspec-continue-change
|
||||
- If `state: "blocked"` (missing artifacts): show message, suggest using `$openspec-continue-change (Codex) or /openspec-continue-change (other agents)` (if it is not installed, run `openspec status --change "<name>" --json` to see the next artifact and `openspec instructions <artifact-id> --change "<name>" --json` for how to create it)
|
||||
- If `state: "all_done"`: congratulate, suggest archive
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
Treat `context` as a required prompt-level input. Read and consider it, and
|
||||
apply relevant project facts, conventions, and constraints while implementing.
|
||||
Treat `operationGuidance` as optional additive advice. Read and consider every
|
||||
entry, and follow entries that are applicable and compatible with the built-in
|
||||
workflow.
|
||||
|
||||
Keep both fields separate from CLI-returned state, missing artifacts, tasks,
|
||||
progress, `contextFiles`, and the built-in `instruction`. They are not
|
||||
evidence of task completion, do not replace the built-in instruction, and do
|
||||
not permit bypassing a blocked state. If context conflicts with the built-in
|
||||
instruction, an explicit user choice, or a CLI-controlled value, report the
|
||||
conflict and preserve the controlling value. If guidance is inapplicable or
|
||||
conflicts with those controlling inputs, do not follow it and explain why.
|
||||
These are prompt-level behavior contracts, not enforceable checks.
|
||||
|
||||
4. **Read context files**
|
||||
|
||||
Read every file path listed under `contextFiles` from the apply instructions output.
|
||||
@@ -60,6 +77,9 @@ Implement tasks from an OpenSpec change.
|
||||
- **spec-driven**: proposal, specs, design, tasks
|
||||
- Other schemas: follow the contextFiles from CLI output
|
||||
|
||||
Do not copy `context` or `operationGuidance` verbatim into implementation
|
||||
files or planning artifacts unless the user separately asks for that content.
|
||||
|
||||
5. **Show current progress**
|
||||
|
||||
Display:
|
||||
@@ -119,7 +139,7 @@ Working on task 4/7: <task description>
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tasks complete! Ready to archive this change.
|
||||
All tasks complete! You can archive this change with `$openspec-archive-change (Codex) or /openspec-archive-change (other agents)`.
|
||||
```
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
@@ -151,6 +171,11 @@ What would you like to do?
|
||||
- Update task checkbox immediately after completing each task
|
||||
- Pause on errors, blockers, or unclear requirements - don't guess
|
||||
- Use contextFiles from CLI output, don't assume specific file names
|
||||
- Do not use context or operation guidance as proof that a task is complete
|
||||
- Apply relevant project context; report conflicts with controlling workflow inputs
|
||||
- Consider every guidance entry; explain any inapplicable or conflicting advice
|
||||
- Do not copy runtime context or operation guidance into implementation files or planning artifacts
|
||||
- Preserve CLI-controlled blocked/ready/all-done behavior and completion criteria
|
||||
|
||||
**Fluid Workflow Integration**
|
||||
|
||||
182
.agents/skills/openspec-archive-change/SKILL.md
Normal file
182
.agents/skills/openspec-archive-change/SKILL.md
Normal file
@@ -0,0 +1,182 @@
|
||||
---
|
||||
name: openspec-archive-change
|
||||
description: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve the full path from each delta spec when resolving its main spec.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
When prompting, show only active changes (not already archived).
|
||||
Include the schema used for each change if available.
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `$openspec-archive-change (Codex) or /openspec-archive-change (other agents) <other>`).
|
||||
|
||||
**Load current archive inputs before the existing archive checks:**
|
||||
|
||||
After resolving the selected change and planning root, run:
|
||||
```bash
|
||||
openspec instructions archive --change "<name>" --json
|
||||
```
|
||||
Keep the same selected-root flags on this command. This lookup is advisory and
|
||||
optional: it only supplies extra prompt inputs, so it must never block archiving.
|
||||
If it exits non-zero or returns invalid JSON — for example on an older CLI that
|
||||
does not support this command yet — continue the archive workflow with no
|
||||
context and no operation guidance. Do not report an error and do not stop.
|
||||
|
||||
A successful response may omit both optional fields. Treat `context` as a
|
||||
required prompt-level input: read and consider it, and apply relevant project
|
||||
facts, conventions, and constraints. Treat `operationGuidance` as optional
|
||||
additive advice: read and consider every entry, and follow entries that are
|
||||
applicable and compatible with the built-in archive workflow.
|
||||
|
||||
Keep both fields separate from built-in steps, explicit user choices, resolved
|
||||
paths, CLI checks, and command contracts. If context conflicts with one of those
|
||||
controlling inputs, report the conflict and preserve the controlling value. If
|
||||
guidance is inapplicable or conflicts with a controlling input, do not follow it
|
||||
and explain why. Do not infer replacement paths, skipped prompts, or flags from
|
||||
either field, and do not copy their text verbatim into specs, change artifacts,
|
||||
or archive summaries unless the user separately asks for it. These are
|
||||
prompt-level behavior contracts, not enforceable checks.
|
||||
|
||||
2. **Check artifact completion status**
|
||||
|
||||
Run `openspec status --change "<name>" --json` to check artifact completion.
|
||||
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context
|
||||
- `artifacts`: List of artifacts with their status (`done`, `skipped`, or other)
|
||||
|
||||
**If any artifacts are neither `done` nor `skipped`** (skipped artifacts satisfy the requirement - the change declares skip_specs):
|
||||
- Display warning listing incomplete artifacts
|
||||
- Ask the user to confirm they want to proceed
|
||||
- Proceed if user confirms
|
||||
|
||||
3. **Check task completion status**
|
||||
|
||||
Read the tasks file (typically `tasks.md`) to check for incomplete tasks.
|
||||
|
||||
Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).
|
||||
|
||||
**If incomplete tasks found:**
|
||||
- Display warning showing count of incomplete tasks
|
||||
- Ask the user to confirm they want to proceed
|
||||
- Proceed if user confirms
|
||||
|
||||
**If no tasks file exists:** Proceed without task-related warning.
|
||||
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
Use `artifactPaths.specs.existingOutputPaths` from status JSON as the only
|
||||
delta-spec source. If the `specs` entry is missing or
|
||||
`existingOutputPaths` is empty, proceed without a sync prompt and do not infer
|
||||
delta specs from other artifacts.
|
||||
|
||||
**If delta specs exist:**
|
||||
- Compare each delta spec with its corresponding main spec at `<planningHome.root>/openspec/specs/<capability-path>/spec.md` (use the store-aware `planningHome.root` from step 2, not a hardcoded repo path)
|
||||
- Determine what changes would be applied (adds, modifications, removals, renames)
|
||||
- Show a combined summary before prompting
|
||||
|
||||
**Prompt options:**
|
||||
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
||||
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
||||
|
||||
Route on the answer:
|
||||
- "Cancel" — stop, do not archive
|
||||
- "Archive without syncing" or "Archive now" — proceed to archive
|
||||
- "Sync now" or "Sync anyway" — sync, then verify (below)
|
||||
- Anything else — ask again rather than archiving
|
||||
|
||||
Before a selected sync writes any main spec, run
|
||||
`openspec instructions specs --change "<name>" --json` once with the same
|
||||
selected-root flags. Require a zero exit status and valid artifact-instruction
|
||||
JSON. If the lookup fails or returns invalid JSON, report the error and stop
|
||||
before writing any main spec or moving the change. A valid response with omitted
|
||||
`rules` is the no-rules case. Apply returned `rules` only to the content and
|
||||
form of main specs produced by this merge; do not use them as archive guidance,
|
||||
change CLI behavior, or copy the rule text into any output file.
|
||||
|
||||
Then run the `openspec-sync-specs` workflow inline (agent-driven intelligent merge) for change '<name>', passing the delta spec analysis and the fetched specs-rule snapshot from above, and wait for it to finish. The inline sync must reuse that snapshot without fetching `specs` instructions again. Do not delegate it to a background task — step 5 would move `changeRoot` out from under a sync that is still reading it, leaving the change archived and the main specs never updated. If your agent can only run it by delegation, delegate synchronously and wait for the result.
|
||||
|
||||
Then re-run the comparison from the top of this step against every capability that has a delta spec in `artifactPaths.specs.existingOutputPaths` — not only the ones the sync reports it touched. A successful sync leaves nothing left to apply, so each capability must now read as already synced:
|
||||
- ADDED requirements present
|
||||
- MODIFIED requirements carrying the scenario and description changes named in the delta, with their other scenarios intact
|
||||
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving `## Requirements` empty), its main spec deleted rather than left empty; a spec the sync deliberately kept and reported is also a match
|
||||
- RENAMED requirements present under the new name and absent under the old one
|
||||
|
||||
If the sync failed, or any capability does not match, report what differs and stop — do not archive. Nothing has moved and `changeRoot` is intact, so the user can fix the mismatch or re-run the sync and start the archive again.
|
||||
|
||||
5. **Perform the archive**
|
||||
|
||||
Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:
|
||||
```bash
|
||||
mkdir -p "<planningHome.changesDir>/archive"
|
||||
```
|
||||
|
||||
Generate the target name: use the change name as-is when it already starts with a `YYYY-MM-DD-` prefix; otherwise prepend the current date as `YYYY-MM-DD-<change-name>`. Never stack a second date (same rule as `openspec archive`).
|
||||
|
||||
**Check if target already exists:**
|
||||
- If yes: Fail with error, suggest renaming existing archive or using different date
|
||||
- If no: Move `changeRoot` to the archive directory
|
||||
|
||||
```bash
|
||||
mv "<changeRoot>" "<planningHome.changesDir>/archive/<target-name>"
|
||||
```
|
||||
|
||||
6. **Display summary**
|
||||
|
||||
Show archive completion summary including:
|
||||
- Change name
|
||||
- Schema that was used
|
||||
- Archive location
|
||||
- Whether specs were synced (if applicable)
|
||||
- Note about any warnings (incomplete artifacts/tasks)
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```markdown
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** the archive path derived from `planningHome.changesDir`/<target-name>/
|
||||
**Specs:** <"✓ Synced to main specs" only if the step 4 verification passed; otherwise "No delta specs" or "Sync skipped">
|
||||
|
||||
<"All artifacts complete. All tasks complete." — or, if archived with warnings, list them instead (e.g. "Archived with 2 incomplete tasks")>
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Announce the selected change; prompt for selection when it is ambiguous
|
||||
- Use artifact graph (openspec status --json) for completion checking
|
||||
- Don't block archive on warnings - just inform and confirm
|
||||
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
||||
- Show clear summary of what happened
|
||||
- If sync is requested, run the `openspec-sync-specs` workflow inline (agent-driven)
|
||||
- Never archive while a spec sync is still in flight — run the sync inline and verify the main specs before moving `changeRoot`
|
||||
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
||||
- Apply relevant runtime context and report conflicts; operation guidance remains advisory
|
||||
- Consider every guidance entry and explain any inapplicable or conflicting advice
|
||||
- Existing CLI checks, resolved paths, prompts, and command contracts are unchanged
|
||||
- Artifact rules constrain only the specs being written and are never operation guidance
|
||||
- Never copy runtime context, operation guidance, or artifact-rule text verbatim into output files
|
||||
@@ -7,16 +7,16 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing. For a new change, scaffold it first as described below.
|
||||
|
||||
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
---
|
||||
|
||||
@@ -94,6 +94,12 @@ This tells you:
|
||||
- Their names, schemas, and status
|
||||
- What the user might be working on
|
||||
|
||||
Then read the project's own context from the resolved root - `<root.path>/openspec/config.yaml` (or `config.yml`). Use the `root.path` returned above, and skip this if neither file exists:
|
||||
- `context`: project background - tech stack, conventions, constraints
|
||||
- `rules`: keyed by artifact id - the entries for an artifact apply only when you write that artifact
|
||||
|
||||
Ground your thinking in these. They are constraints for you to follow, not content to reproduce: do NOT copy them into the conversation or into any artifact you create.
|
||||
|
||||
### When no change exists
|
||||
|
||||
Think freely. When insights crystallize, you might offer:
|
||||
@@ -101,6 +107,15 @@ Think freely. When insights crystallize, you might offer:
|
||||
- "This feels solid enough to start a change. Want me to create a proposal?"
|
||||
- Or keep exploring - no pressure to formalize
|
||||
|
||||
If the user asks you to capture the exploration as a new change, transition seamlessly into the requested capture:
|
||||
|
||||
1. Run `openspec new change "<name>"` (with `--store <id>` when applicable) before creating any artifacts. Never create a new change directory under `openspec/changes/` by hand; the CLI scaffold creates required metadata such as `.openspec.yaml`. Keep the selected `--store <id>` on every applicable follow-up `status` and `instructions` command.
|
||||
2. Run `openspec status --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store), then process the requested artifacts in dependency order. For each requested artifact that is `ready`, run `openspec instructions "<artifact-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store). Before creating a requested artifact, evaluate any condition in its own `instruction` against the explored change; record a deliberate skip instead when the condition does not apply. If a requested artifact is blocked by a direct prerequisite the user did not request, run `openspec instructions "<prerequisite-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) for that prerequisite whether it is `ready` or `blocked`. If its own `instruction` states a condition, evaluate that condition against the explored change and record a deliberate skip only when the condition does not apply. If the condition applies, or the prerequisite is not conditional, treat it as a normal prerequisite and ask before expanding the capture. Do not create an unrequested prerequisite unless the user approves.
|
||||
3. Follow the returned `template` and `instruction` fields. Read completed dependency files listed in `dependencies`, and apply `context` and `rules` as constraints without copying them into the artifact. If the instruction delegates creation to a specific skill or command, invoke it; otherwise write the artifact to `resolvedOutputPath`, using the instruction to choose a concrete path when it is a glob. Verify that the selected concrete output exists.
|
||||
4. After creating each artifact, re-run `openspec status --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) and continue until every requested artifact is `done`, `skipped`, or was deliberately skipped because its own `instruction` stated a condition that did not apply. Tell the user about a deliberate conditional skip, remember it, and do not reconsider it. Dependencies are enablers, not gates: if a requested artifact is still `blocked` only because you deliberately skipped a conditional prerequisite, run `openspec instructions "<artifact-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) despite the blocked status, then create it using step 3 only when those recorded conditional skips are its sole missing dependencies. If a requested artifact is blocked by a prerequisite the user did not ask to capture and cannot be conditionally skipped, explain that dependency and ask before expanding the capture.
|
||||
|
||||
Capture the artifact(s) the user requested without asking them to invoke another workflow command. If they asked only to start a change, stop after scaffolding and show its status.
|
||||
|
||||
### When a change exists
|
||||
|
||||
If the user mentions a change or you detect one is relevant:
|
||||
@@ -116,14 +131,16 @@ If the user mentions a change or you detect one is relevant:
|
||||
|
||||
3. **Offer to capture when decisions are made**
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|----------------------------|--------------------------------|
|
||||
| New requirement discovered | `specs/<capability>/spec.md` |
|
||||
| Requirement changed | `specs/<capability>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve an existing capability's full path and follow the project's established organization for new capabilities.
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|----------------------------|-------------------------------------|
|
||||
| New requirement discovered | `specs/<capability-path>/spec.md` |
|
||||
| Requirement changed | `specs/<capability-path>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
@@ -203,7 +220,7 @@ You: [reads codebase]
|
||||
|
||||
**User is stuck mid-implementation:**
|
||||
```
|
||||
User: /opsx:explore add-auth-system
|
||||
User: $openspec-explore (Codex) or /openspec-explore (other agents) add-auth-system
|
||||
The OAuth integration is more complex than expected
|
||||
|
||||
You: [reads change artifacts]
|
||||
@@ -285,6 +302,7 @@ But this summary is optional. Sometimes the thinking IS the value.
|
||||
- **Don't rush** - Discovery is thinking time, not task time
|
||||
- **Don't force structure** - Let patterns emerge naturally
|
||||
- **Don't auto-capture** - Offer to save insights, don't just do it
|
||||
- **Don't manually scaffold changes** - Never create a new change directory under `openspec/changes/` by hand. Always use `openspec new change "<name>"` (with `--store <id>` when applicable) so required metadata such as `.openspec.yaml` is created before writing artifacts.
|
||||
- **Do visualize** - A good diagram is worth many paragraphs
|
||||
- **Do explore the codebase** - Ground discussions in reality
|
||||
- **Do question assumptions** - Including the user's and your own
|
||||
149
.agents/skills/openspec-propose/SKILL.md
Normal file
149
.agents/skills/openspec-propose/SKILL.md
Normal file
@@ -0,0 +1,149 @@
|
||||
---
|
||||
name: openspec-propose
|
||||
description: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Propose a new change - create the change and generate all artifacts in one step.
|
||||
|
||||
**Planning boundary**: This workflow creates planning artifacts only. The user request that selected or triggered this workflow authorizes planning only, even if it asks to build or fix something. Do not edit project code. After the planning artifacts are complete, stop. Do not start implementation in the same response, even if the initial request asks for it. Wait for a new user request after the artifacts are presented; then start the apply workflow.
|
||||
|
||||
I'll create a change with the artifacts your schema defines. With the default spec-driven schema that is:
|
||||
- proposal.md (what & why)
|
||||
- `specs/<capability-path>/spec.md` (what the system must do - a delta, not the main spec)
|
||||
- design.md (how)
|
||||
- tasks.md (implementation steps)
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve an existing capability's full path and follow the project's established organization for new capabilities.
|
||||
|
||||
When the user is ready to implement, they must start the apply workflow explicitly.
|
||||
|
||||
---
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Understand the request and clarify material ambiguity**
|
||||
|
||||
If no clear input is provided, ask the user (open-ended, no preset options):
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
If the request contains ambiguity that would materially affect scope, externally observable behavior, compatibility, or acceptance criteria, ask the user before creating the change. For minor details, make a reasonable assumption and record it in the planning artifacts.
|
||||
|
||||
2. **Determine the workflow schema**
|
||||
|
||||
Use the configured default schema unless the user explicitly requests a different workflow.
|
||||
|
||||
**Use a different schema only if the user:**
|
||||
- Explicitly requests a specific schema by name → use `--schema <schema-name>`
|
||||
- Asks to "show workflows" or asks "what workflows" exist → resolve the authoritative root by running `openspec context --json` from the current working directory. If the user explicitly selected a registered store, use `openspec context --json --store "<store-id>"`. Then run `openspec schemas --json` with its working directory set to the returned `root.path` and let them choose. This preserves roots selected by a local `store:` pointer or the global `defaultStore`; `schemas` does not accept `--store`. If context reports only `no_openspec_root`, run `openspec schemas --json` from the current working directory instead. Do not use this fallback for invalid or unavailable stores.
|
||||
|
||||
Otherwise, omit `--schema` to preserve the configured default.
|
||||
|
||||
3. **Create the change directory**
|
||||
|
||||
Choose one schema form below. If a registered store is selected, append `--store "<store-id>"` to that command and each later OpenSpec command shown below that accepts `--store`.
|
||||
|
||||
Using the configured default:
|
||||
```bash
|
||||
openspec new change "<name>"
|
||||
```
|
||||
|
||||
Using an explicitly requested schema:
|
||||
```bash
|
||||
openspec new change "<name>" --schema "<schema-name>"
|
||||
```
|
||||
This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.
|
||||
|
||||
4. **Get the artifact build order**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to get:
|
||||
- `applyRequires`: array of artifact IDs needed before implementation (e.g., `["tasks"]`)
|
||||
- `artifacts`: list of all artifacts, each with its `status` and its `requires` edges (the artifact IDs it directly depends on)
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.
|
||||
|
||||
5. **Create every artifact in the required set**
|
||||
|
||||
Use a todo list to track progress through the artifacts.
|
||||
|
||||
Loop through artifacts in dependency order (artifacts with no pending dependencies first):
|
||||
|
||||
a. **For each artifact that is `ready` (dependencies satisfied)**:
|
||||
- Get instructions:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- The instructions JSON includes:
|
||||
- `context`: Project background (constraints for you - do NOT include in output)
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance for this artifact type
|
||||
- `skipped`/`warning`: present when the change declares skip_specs and this artifact must NOT be created - stop and pick another artifact
|
||||
- `resolvedOutputPath`: Resolved path or pattern to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- Read any completed dependency files for context - always re-read them from disk, even if you saw them earlier in the conversation (the user may have edited them)
|
||||
- If the `instruction` field delegates creation to a specific skill or command, invoke it to produce the artifact instead of writing the file yourself, then verify the artifact file exists at `resolvedOutputPath`
|
||||
- Otherwise create the artifact file using `template` as the structure and write it to `resolvedOutputPath`. If `resolvedOutputPath` is a glob, follow `instruction` to choose the concrete file path
|
||||
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
||||
- Show brief progress: "Created <artifact-id>"
|
||||
|
||||
b. **Continue until every artifact in the required set exists (not just `apply.requires`)**
|
||||
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
||||
- The required set is `applyRequires` plus every artifact reachable from those by following the `requires` edges in `status --json` - walk them transitively (spec-driven closes over proposal, specs, design, tasks). Leave artifacts outside that set alone
|
||||
- `status` is file-existence only, so an `applyRequires` artifact reading `done` does NOT mean its dependencies exist - writing `tasks.md` early marks `tasks` done while `specs` was never written. Use each artifact's `requires` edges, not its `status`, to build the required set: a `done` artifact still lists what it depends on
|
||||
- An artifact already reading `status: "skipped"` is satisfied: the change declares `skip_specs` in `.openspec.yaml`, so its files must NOT exist. Never try to create one
|
||||
- Create every artifact in the required set that is missing, then re-check - creating one can unblock others
|
||||
- Skip one only when `status` already reports it `skipped`, or when its own `instruction` says it is conditional: run `openspec instructions <artifact-id> --change "<name>" --json` and skip only if its `instruction` field marks it optional (e.g. "create only if..."). Spec-driven's `design.md` qualifies; `specs` qualifies only via the `skipped` status above, never by your own judgment. Tell the user, and do not reconsider it
|
||||
- Dependencies are enablers, not gates: if a required artifact is still `blocked` only because you skipped a conditional dependency, write it anyway
|
||||
- Stop when every artifact in the required set is `done`, `skipped`, or was deliberately skipped
|
||||
|
||||
c. **If an artifact requires user input** (unclear context):
|
||||
- Ask the user to clarify
|
||||
- Then continue with creation
|
||||
|
||||
6. **Show final status**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
|
||||
**Output**
|
||||
|
||||
After completing all artifacts, summarize:
|
||||
- Change name and location
|
||||
- List of artifacts created with brief descriptions, plus any conditional artifact you skipped and why
|
||||
- What's ready: "All artifacts needed for implementation are ready."
|
||||
- Prompt: "The artifacts are ready for review. When you are ready, run `$openspec-apply-change (Codex) or /openspec-apply-change (other agents)` or ask me to apply this change."
|
||||
|
||||
**Artifact Creation Guidelines**
|
||||
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type - it is the authoritative guidance, even for familiar artifact names
|
||||
- If the `instruction` field directs you to use a specific skill or command to create the artifact, invoke it instead of writing the artifact directly
|
||||
- The schema defines what each artifact should contain - follow it
|
||||
- Read dependency artifacts for context before creating new ones
|
||||
- Use `template` as the structure for your output file - fill in its sections
|
||||
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
||||
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
||||
- These guide what you write, but should never appear in the output
|
||||
|
||||
**Guardrails**
|
||||
- The request that invoked this workflow authorizes planning only. Any implementation or apply instruction in that request does not carry forward. Do NOT implement the change, start the apply workflow, or edit project code during this workflow. After presenting the artifacts, stop and wait for a new user request to start the apply workflow
|
||||
- Create every artifact the apply phase transitively depends on, not just the ids listed in `apply.requires`
|
||||
- Always read dependency artifacts before creating a new one - re-read from disk, not from conversation memory (files may have changed since you last saw them)
|
||||
- Ask about ambiguities that would materially change scope, externally observable behavior, compatibility, or acceptance criteria; for minor details, make reasonable assumptions and record them
|
||||
- If a change with that name already exists, ask if user wants to continue it or create a new one
|
||||
- Verify each artifact file exists after writing before proceeding to next
|
||||
262
.agents/skills/openspec-sync-specs/SKILL.md
Normal file
262
.agents/skills/openspec-sync-specs/SKILL.md
Normal file
@@ -0,0 +1,262 @@
|
||||
---
|
||||
name: openspec-sync-specs
|
||||
description: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Sync delta specs from a change to main specs.
|
||||
|
||||
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve the full path from each delta spec when resolving its main spec.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
When prompting, show changes that have delta specs (under `specs/` directory).
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `$openspec-sync-specs (Codex) or /openspec-sync-specs (other agents) <other>`).
|
||||
|
||||
2. **Resolve change context**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
|
||||
The JSON includes `planningHome.root`. Main specs live under `<planningHome.root>/openspec/specs/` — use that (store-aware) root for every main-spec path below, not a hardcoded repo path. When a store is selected it points at the store, not the current repository.
|
||||
|
||||
3. **Find delta specs**
|
||||
|
||||
Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the
|
||||
only source of delta spec paths. If the `specs` entry is missing or
|
||||
`existingOutputPaths` is empty, report that there are no delta specs to sync,
|
||||
do not infer them from other artifacts, and stop without requesting artifact
|
||||
instructions or writing a main spec.
|
||||
|
||||
Sync every path in `existingOutputPaths` unless the caller narrowed the set.
|
||||
A caller narrows it by naming an explicit list of complete entries from
|
||||
`existingOutputPaths` — copy those absolute values verbatim. Archive does
|
||||
this inline, and a user can too (for example, by selecting the entry ending
|
||||
in `/specs/billing/invoices/spec.md`).
|
||||
Then sync only the named paths and leave the remaining delta specs untouched:
|
||||
bulk archive excludes a delta whose implementation it could not find, and
|
||||
syncing it anyway would write a main spec the caller deliberately withheld.
|
||||
Carry that narrowed selection through step 4; never widen it back to the full
|
||||
list. If a named path is not in `existingOutputPaths`, do not sync it —
|
||||
report it and stop, rather than dropping it silently. If the named list is
|
||||
empty, report that there is nothing to sync and stop without writing a main
|
||||
spec.
|
||||
|
||||
Each delta spec file contains sections like:
|
||||
- `## ADDED Requirements` - New requirements to add
|
||||
- `## MODIFIED Requirements` - Changes to existing requirements
|
||||
- `## REMOVED Requirements` - Requirements to remove
|
||||
- `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)
|
||||
|
||||
If no delta specs found, inform user and stop.
|
||||
|
||||
4. **For each delta spec, apply changes to main specs**
|
||||
|
||||
Before the first main-spec write, obtain one current specs-rule snapshot:
|
||||
- If archive invoked this workflow inline and supplied a valid snapshot from
|
||||
`openspec instructions specs --change "<name>" --json`, reuse it and do not
|
||||
fetch the same instructions again.
|
||||
- Otherwise run that command once now with the same selected-root flags.
|
||||
- If the direct lookup exits non-zero or returns invalid artifact-instruction
|
||||
JSON, report the error and stop before writing any main spec. Do not treat the
|
||||
failure as an absent rule set.
|
||||
- A valid response with omitted `rules` means no artifact rules are configured
|
||||
and the existing semantic merge continues.
|
||||
|
||||
Apply returned `rules` only to the content and form of the main specs produced
|
||||
by this merge. Artifact rules are not operation guidance and cannot change
|
||||
selected roots, delta paths, CLI checks, or workflow steps. Use their text as
|
||||
constraints without copying it verbatim into a main spec or summary.
|
||||
|
||||
For each capability delta spec path selected in step 3 — the full `existingOutputPaths` list, or the narrowed subset when a caller supplied one (these may belong to a selected store, not the repo):
|
||||
|
||||
a. **Read the delta spec** to understand the intended changes
|
||||
|
||||
b. **Read the main spec** at `<planningHome.root>/openspec/specs/<capability-path>/spec.md` (may not exist yet)
|
||||
|
||||
c. **Apply changes intelligently**:
|
||||
|
||||
**ADDED Requirements:**
|
||||
- If requirement doesn't exist in main spec → add it
|
||||
- If requirement already exists → update it to match (treat as implicit MODIFIED)
|
||||
|
||||
**MODIFIED Requirements:**
|
||||
- Find the requirement in main spec
|
||||
- Apply the changes - this can be:
|
||||
- Adding new scenarios the main spec does not have yet
|
||||
- Modifying existing scenarios
|
||||
- Changing the requirement description
|
||||
- Preserve scenarios/content not mentioned in the delta
|
||||
|
||||
**REMOVED Requirements:**
|
||||
- Remove the entire requirement block from main spec
|
||||
- Retiring the capability. Delete the whole `spec.md` - and the directory once
|
||||
nothing else is left in it - only when ALL of these hold:
|
||||
1. removing the requirements *this run* left no requirement blocks;
|
||||
2. the rest of the spec is well-formed (it still has a `## Purpose`);
|
||||
3. the main spec was not already empty before this sync - if you removed
|
||||
nothing, change nothing;
|
||||
4. every other nonblank line in the whole file is accounted for as the
|
||||
title, Purpose, Requirements header, or a canonical requirement's
|
||||
statement, scenarios, or fenced examples;
|
||||
5. the change's `.openspec.yaml` declares `retire_capabilities: true`;
|
||||
6. the `spec.md` resolves inside the real specs root (do not follow a
|
||||
capability-directory symlink to delete an external file).
|
||||
If removing the selected requirements would leave no requirement blocks and
|
||||
any retirement condition is not satisfied, do not modify the main spec. Stop
|
||||
the sync for that capability, report the blocking condition, and tell the user
|
||||
how to resolve it. Never write or leave an empty `## Requirements` section.
|
||||
When only the marker is missing, say that too - it is the one thing the user
|
||||
can add to make the retirement go through.
|
||||
- Deleting the file also deletes its `## Purpose`; any other section blocks
|
||||
retirement. Name Purpose when you report the retirement. Include a pasteable
|
||||
`git checkout` only when the spec lived in the caller's checkout;
|
||||
otherwise give checkout-scoped recovery guidance.
|
||||
|
||||
**RENAMED Requirements:**
|
||||
- Find the FROM requirement, rename to TO
|
||||
|
||||
**`## Purpose` in the delta:**
|
||||
- The main spec already has one and it is authoritative - leave it alone
|
||||
(this is what `openspec archive` does; it warns and moves on)
|
||||
|
||||
d. **Create new main spec** if capability doesn't exist yet:
|
||||
- Create `<planningHome.root>/openspec/specs/<capability-path>/spec.md`
|
||||
- Add Purpose section: copy the delta's `## Purpose` body verbatim when it has one
|
||||
(this is what `openspec archive` does); only write a brief TBD placeholder when it does not
|
||||
- Add Requirements section with the ADDED requirements
|
||||
- Follow the **Main Spec Format Reference** below
|
||||
|
||||
5. **Validate updated main specs**
|
||||
|
||||
Run `openspec validate --specs` with the same selected-root flags used earlier.
|
||||
If validation fails, report the problems and do not claim the sync succeeded.
|
||||
|
||||
6. **Show summary**
|
||||
|
||||
After applying all changes, summarize:
|
||||
- Which capabilities were updated
|
||||
- What changes were made (requirements added/modified/removed/renamed)
|
||||
- Any new main spec left with a TBD Purpose placeholder, so it gets written
|
||||
now rather than lingering
|
||||
- Any capability retired, naming the deleted `spec.md`, its Purpose, and
|
||||
either a pasteable `git checkout` or checkout-scoped recovery guidance
|
||||
|
||||
**Delta Spec Format Reference**
|
||||
|
||||
```markdown
|
||||
## Purpose
|
||||
|
||||
Only on a delta that introduces a brand-new capability. Seeds the new main spec.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: New Feature
|
||||
The system SHALL do something new.
|
||||
|
||||
#### Scenario: Basic case
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Existing Feature
|
||||
The system SHALL keep doing the existing thing, now also handling A.
|
||||
|
||||
#### Scenario: Scenario the main spec already has
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
|
||||
#### Scenario: New scenario to add
|
||||
- **WHEN** user does A
|
||||
- **THEN** system does B
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: Deprecated Feature
|
||||
|
||||
## RENAMED Requirements
|
||||
|
||||
- FROM: `### Requirement: Old Name`
|
||||
- TO: `### Requirement: New Name`
|
||||
```
|
||||
|
||||
**Main Spec Format Reference**
|
||||
|
||||
Main specs are what the delta merges INTO. They must never contain delta operation headers (`## ADDED/MODIFIED/REMOVED/RENAMED Requirements`) - after syncing, every requirement lives under a single `## Requirements` section:
|
||||
|
||||
```markdown
|
||||
# <capability> Specification
|
||||
|
||||
## Purpose
|
||||
Short description of what this capability does and why it exists.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: New Feature
|
||||
The system SHALL do something new.
|
||||
|
||||
#### Scenario: Basic case
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
```
|
||||
|
||||
**Key Principle: Intelligent Merging**
|
||||
|
||||
Unlike programmatic merging, you merge rather than overwrite:
|
||||
- A MODIFIED block carries the whole requirement - body plus every scenario that survives the change. `openspec validate` and `openspec archive` both reject one that drops a scenario the main spec still has.
|
||||
- Keep anything the delta does not mention, in the main spec's existing order
|
||||
- Use your judgment to merge changes sensibly
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```markdown
|
||||
## Specs Synced: <change-name>
|
||||
|
||||
Updated main specs:
|
||||
|
||||
**<capability-1>**:
|
||||
- Added requirement: "New Feature"
|
||||
- Modified requirement: "Existing Feature" (added 1 scenario)
|
||||
|
||||
**<capability-2>**:
|
||||
- Created new spec file
|
||||
- Added requirement: "Another Feature"
|
||||
|
||||
Main specs are now updated. The change remains active - archive when implementation is complete.
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Read both delta and main specs before making changes
|
||||
- Preserve existing content not mentioned in delta
|
||||
- Never copy a delta file into a main spec as-is - merge its content so the main spec keeps the Main Spec Format Reference structure, with no delta operation headers
|
||||
- If something is unclear, ask for clarification
|
||||
- Show what you're changing as you go
|
||||
- The operation should be idempotent - running twice should give same result
|
||||
- Use only `artifactPaths.specs.existingOutputPaths`; never infer delta specs from unrelated artifacts
|
||||
- Honor a caller-supplied subset of `existingOutputPaths`; never widen it back to the full list
|
||||
- Fetch specs instructions once for direct sync, or reuse the archive-supplied snapshot inline
|
||||
- Stop before every main-spec write on a non-zero or invalid JSON specs-instruction response
|
||||
- Artifact rules constrain only the specs being written and are never copied into output files
|
||||
@@ -7,22 +7,27 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Revise a change's existing planning artifacts and keep them coherent. Never edit code.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
`$openspec-continue-change (Codex) or /openspec-continue-change (other agents)` is an expanded-profile workflow and may not be installed. Before suggesting it anywhere below, verify that it is available. If it is unavailable, `openspec status --change "<name>" --json` shows the next artifact and `openspec instructions "<artifact-id>" --change "<name>" --json` explains how to create it.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
1. **Select the change**
|
||||
|
||||
Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes sorted by most recently modified, and ask the user to select one
|
||||
|
||||
Present the top 3-4 most recently modified changes as options, showing:
|
||||
When prompting, present the top 3-4 most recently modified changes as options, showing:
|
||||
- Change name
|
||||
- Schema (from `schema` field if present, otherwise "spec-driven")
|
||||
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
||||
@@ -30,7 +35,7 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
|
||||
|
||||
Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to update.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `$openspec-update-change (Codex) or /openspec-update-change (other agents) <other>`).
|
||||
|
||||
2. **Get the change's artifacts**
|
||||
```bash
|
||||
@@ -38,8 +43,8 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
|
||||
```
|
||||
Parse the JSON to understand current state. The response includes:
|
||||
- `schemaName`: The workflow schema being used (e.g., "spec-driven")
|
||||
- `artifacts`: Array of artifacts with their status ("done", "ready", "blocked")
|
||||
- `isComplete`: Boolean indicating if all artifacts are complete
|
||||
- `artifacts`: Array of artifacts with their status ("done", "skipped", "ready", "blocked")
|
||||
- `isPlanningComplete`: Boolean indicating if all planning artifacts are complete. Older CLI versions expose the same value as `isComplete`.
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.
|
||||
|
||||
The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.
|
||||
@@ -54,7 +59,7 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
|
||||
- Read the artifact(s) the request touches and the change's other existing artifacts.
|
||||
- Apply the requested edit. Then check every other existing artifact against it - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.
|
||||
- Note everything that is now inconsistent, missing, or contradictory.
|
||||
- Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/opsx:continue` to create them.
|
||||
- Revise only files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `$openspec-continue-change (Codex) or /openspec-continue-change (other agents)` to create them.
|
||||
- If the change is already coherent, say so and make no edits.
|
||||
|
||||
5. **Confirm and apply, one artifact at a time**
|
||||
@@ -62,25 +67,25 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
|
||||
- If the user rejects a revision, do not write it - leave that artifact unchanged.
|
||||
- When a substantial rewrite is needed, get that artifact's rules and template first:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
openspec instructions "<artifact-id>" --change "<name>" --json
|
||||
```
|
||||
|
||||
6. **Point to the next step (guidance only - NEVER act on it)**
|
||||
- Artifacts still missing -> suggest `/opsx:continue` to create them.
|
||||
- Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/opsx:apply` to carry the delta into code.
|
||||
- Everything done and implemented -> suggest `/opsx:archive`.
|
||||
- Artifacts still missing -> suggest `$openspec-continue-change (Codex) or /openspec-continue-change (other agents)` to create them.
|
||||
- Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `$openspec-apply-change (Codex) or /openspec-apply-change (other agents)` to carry the delta into code.
|
||||
- Everything done and implemented -> suggest `$openspec-archive-change (Codex) or /openspec-archive-change (other agents)`.
|
||||
|
||||
**Output**
|
||||
|
||||
After each invocation, show:
|
||||
- Which artifacts were revised (and which proposed revisions were rejected)
|
||||
- Anything deferred to `/opsx:continue` (not-yet-created artifacts or files)
|
||||
- Anything deferred to `$openspec-continue-change (Codex) or /openspec-continue-change (other agents)` (not-yet-created artifacts or files)
|
||||
- Where the change stands and the recommended next command
|
||||
|
||||
**Guardrails**
|
||||
- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/opsx:apply`.
|
||||
- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `$openspec-apply-change (Codex) or /openspec-apply-change (other agents)`.
|
||||
- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.
|
||||
- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.
|
||||
- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.
|
||||
- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `$openspec-continue-change (Codex) or /openspec-continue-change (other agents)`'s job.
|
||||
- Confirm every edit with the user before writing.
|
||||
- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the "Update vs. Start Fresh" heuristic).
|
||||
- If the request changes the change's *intent* rather than refining it, first verify whether the expanded-profile `$openspec-new-change (Codex) or /openspec-new-change (other agents)` workflow is available. If it is, recommend starting fresh with `$openspec-new-change (Codex) or /openspec-new-change (other agents)` (the "Update vs. Start Fresh" heuristic). If it is unavailable, ask for a distinct unused change name and recommend `openspec new change "<new-change-name>"` instead.
|
||||
@@ -1,13 +1,15 @@
|
||||
# Logic Prototype
|
||||
|
||||
A tiny interactive terminal app that lets the user drive a state model by hand. Use this when the question is about **business logic, state transitions, or data shape** — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases.
|
||||
A single, self-contained HTML file — a **shareable demo** — that lets anyone drive a state model by clicking buttons. Use this when the question is about **business logic, state transitions, or data shape** — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases.
|
||||
|
||||
Because it's one file with nothing to install, you can hand it to a non-developer — a designer, a PM, a domain expert — and let them feel the model for themselves. So it speaks their language, not the code's.
|
||||
|
||||
## When this is the right shape
|
||||
|
||||
- "I'm not sure if this state machine handles the edge case where X then Y."
|
||||
- "Does this data model actually let me represent the case where..."
|
||||
- "I want to feel out what the API should look like before writing it."
|
||||
- Anything where the user wants to **press buttons and watch state change**.
|
||||
- Anything where someone wants to **press buttons and watch state change**.
|
||||
|
||||
If the question is "what should this look like" — wrong branch. Use [UI.md](UI.md).
|
||||
|
||||
@@ -15,17 +17,11 @@ If the question is "what should this look like" — wrong branch. Use [UI.md](UI
|
||||
|
||||
### 1. State the question
|
||||
|
||||
Before writing code, write down what state model and what question you're prototyping. One paragraph, in the prototype's README or a comment at the top of the file. A logic prototype that answers the wrong question is pure waste — make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK.
|
||||
Before writing code, write down what state model and what question you're prototyping. One paragraph, at the top of the demo (in a visible intro, not just a comment). A logic prototype that answers the wrong question is pure waste — make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK.
|
||||
|
||||
### 2. Pick the language
|
||||
### 2. Isolate the logic in a portable module
|
||||
|
||||
Use whatever the host project uses. If the project has no obvious runtime (e.g. a docs repo), ask.
|
||||
|
||||
Match the project's existing conventions for tooling — don't add a new package manager or runtime just for the prototype.
|
||||
|
||||
### 3. Isolate the logic in a portable module
|
||||
|
||||
Put the actual logic — the bit that's answering the question — behind a small, pure interface that could be lifted out and dropped into the real codebase later. The TUI around it is throwaway; the logic module shouldn't be.
|
||||
Put the actual logic — the bit that's answering the question — in a single `<script>` block written as a small, pure module that could be lifted out and dropped into the real codebase later. The page around it is throwaway; this module isn't.
|
||||
|
||||
The right shape depends on the question:
|
||||
|
||||
@@ -34,46 +30,38 @@ The right shape depends on the question:
|
||||
- **A small set of pure functions** over a plain data type. Good when there's no implicit current state — just transformations.
|
||||
- **A class or module with a clear method surface** when the logic genuinely owns ongoing internal state.
|
||||
|
||||
Pick whichever shape best fits the question being asked, *not* whichever is easiest to wire to a TUI. Keep it pure: no I/O, no terminal code, no `console.log` for control flow. The TUI imports it and calls into it; nothing flows the other direction.
|
||||
Pick whichever shape best fits the question being asked, *not* whichever is easiest to wire to a page. Keep it pure: no DOM, no `document`, no button handlers reaching inside it. The page calls into it; nothing flows the other direction. This is what makes the prototype useful past its own lifetime: once the question's answered, the validated reducer / machine / function set lifts into the real module on its own.
|
||||
|
||||
This is what makes the prototype useful past its own lifetime: when the question's been answered, the validated reducer / machine / function set can be lifted into the real module on its own.
|
||||
### 3. Build the shareable HTML file
|
||||
|
||||
### 4. Build the smallest TUI that exposes the state
|
||||
One file, plain HTML/CSS/JS — no framework, no bundler, no server, everything inline so it opens by double-click and survives being emailed around. Anyone should be able to run it by opening it.
|
||||
|
||||
Build it as a **lightweight TUI** — on every tick, clear the screen (`console.clear()` / `print("\033[2J\033[H")` / equivalent) and re-render the whole frame. The user should always see one stable view, not an ever-growing scrollback.
|
||||
Write it for a non-developer. Every label is in **domain language**, not code — buttons and state read like the business, not the reducer. Explain in plain words what's happening.
|
||||
|
||||
Each frame has two parts, in this order:
|
||||
Lay it out with a clean hierarchy, top to bottom:
|
||||
|
||||
1. **Current state**, pretty-printed and diff-friendly (one field per line, or formatted JSON). Use **bold** for field names or section headers and **dim** for less important context (timestamps, IDs, derived values). Native ANSI escape codes are fine — `\x1b[1m` bold, `\x1b[2m` dim, `\x1b[0m` reset. No need to pull in a styling library unless one is already in the project.
|
||||
2. **Keyboard shortcuts**, listed at the bottom: `[a] add user [d] delete user [t] tick clock [q] quit`. Bold the key, dim the description, or vice-versa — whatever reads cleanly.
|
||||
1. **Title and one-line explanation** of what this demo lets you explore (the question from step 1).
|
||||
2. **Current state** — the full relevant state, rendered as a readable panel (labelled fields, not a raw JSON dump), re-rendered after every click so the change is visible. Where it helps a non-developer follow, call out what just changed.
|
||||
3. **Free-play buttons** — one button per action, always available, so anyone can poke at the model in any order. Each click dispatches its action and re-renders the state.
|
||||
4. **Guided walkthroughs** — a set of **scenarios**, one per tab. Each tab holds a short plain-language description of the scenario — the situation it sets up and what to watch for — and underneath it, the ordered **buttons to press** for that scenario. Each step is a real button: clicking it performs that action and moves to the next step. Starting a walkthrough resets to a known initial state so the scenario runs the same way every time.
|
||||
|
||||
Behaviour:
|
||||
Choose scenarios that demonstrate the awkward cases — the happy path, a tricky edge case, an attempt at something that should be illegal — the ones hard to reason about on paper.
|
||||
|
||||
1. **Initialise state** — a single in-memory object/struct. Render the first frame on start.
|
||||
2. **Read one keystroke (or one line)** at a time, dispatch to a handler that mutates state.
|
||||
3. **Re-render** the full frame after every action — don't append, replace.
|
||||
4. **Loop until quit.**
|
||||
Keep it beautiful but restrained: clean typography, generous spacing, one accent colour. No animations, no gimmicks — nothing that competes with the state and the buttons.
|
||||
|
||||
The whole frame should fit on one screen.
|
||||
### 4. Hand it over
|
||||
|
||||
### 5. Make it runnable in one command
|
||||
Send them the file, or open it for them. They'll click through the walkthroughs and free-play whenever they get to it; the interesting moments are when they say "wait, that shouldn't be possible" or "huh, I assumed X would be different" — those are the bugs in the _idea_, which is the whole point. If they want new actions or a new scenario, add them. Prototypes evolve.
|
||||
|
||||
Add a script to the project's existing task runner (`package.json` scripts, `Makefile`, `justfile`, `pyproject.toml`). The user should run `pnpm run <prototype-name>` or equivalent — never need to remember a path.
|
||||
### 5. Capture the answer and the prototype
|
||||
|
||||
If the host project has no task runner, just put the command at the top of the prototype's README.
|
||||
|
||||
### 6. Hand it over
|
||||
|
||||
Give the user the run command. They'll drive it themselves; the interesting moments are when they say "wait, that shouldn't be possible" or "huh, I assumed X would be different" — those are the bugs in the _idea_, which is the whole point. If they want new actions added, add them. Prototypes evolve.
|
||||
|
||||
### 7. Capture the answer and the prototype
|
||||
|
||||
Once the prototype has answered its question, capture the answer, then capture the prototype the way the [SKILL](SKILL.md) describes. The logic-specific mapping: the validated reducer / machine / function set lifts into the real module (the decision, absorbed); the TUI shell rides along to the throwaway branch that keeps the prototype as a primary source.
|
||||
Once the prototype has answered its question, capture the answer, then capture the prototype the way the [SKILL](SKILL.md) describes. The logic-specific mapping: the validated reducer / machine / function set lifts into the real module (the decision, absorbed); the HTML shell rides along to the throwaway branch that keeps the prototype as a primary source — and being one self-contained file, it stays trivially re-runnable there.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- **Don't add tests.** A prototype that needs tests is no longer a prototype.
|
||||
- **Don't wire it to the real database.** Use an in-memory store unless the question is specifically about persistence.
|
||||
- **Don't wire it to the real database.** Use in-memory state unless the question is specifically about persistence.
|
||||
- **Don't generalise.** No "what if we wanted to support X later." The prototype answers one question.
|
||||
- **Don't blur the logic and the TUI together.** If the reducer / state machine references `console.log`, prompts, or terminal escape codes, it's no longer portable. Keep the TUI as a thin shell over a pure module.
|
||||
- **Don't ship the TUI shell into production.** The shell is optimised for being driven by hand from a terminal. The logic module behind it is the bit worth keeping.
|
||||
- **Don't blur the logic and the page together.** If the pure module references the DOM, `document`, or button handlers, it's no longer liftable. Keep the page as a thin shell over a pure module.
|
||||
- **Don't reach for a framework, bundler, or server.** One file the recipient double-clicks; a React app or a dev server defeats "shareable".
|
||||
- **Don't ship the HTML shell into production.** The page is optimised for being clicked through by hand. The logic module behind it is the bit worth keeping.
|
||||
|
||||
@@ -11,7 +11,7 @@ A prototype is **throwaway code that answers a question**. The question decides
|
||||
|
||||
Identify which question is being answered — from the user's prompt, the surrounding code, or by asking if the user is around:
|
||||
|
||||
- **"Does this logic / state model feel right?"** → [LOGIC.md](LOGIC.md). Build a tiny interactive terminal app that pushes the state machine through cases that are hard to reason about on paper.
|
||||
- **"Does this logic / state model feel right?"** → [LOGIC.md](LOGIC.md). Build a single shareable HTML file — free-play buttons plus tabbed guided walkthroughs — that pushes the state machine through cases that are hard to reason about on paper, and that a non-developer can drive.
|
||||
- **"What should this look like?"** → [UI.md](UI.md). Generate several radically different UI variations on a single route, switchable via a URL search param and a floating bottom bar.
|
||||
|
||||
The two branches produce very different artifacts — getting this wrong wastes the whole prototype. If the question is genuinely ambiguous and the user isn't reachable, default to whichever branch better matches the surrounding code (a backend module → logic; a page or component → UI) and state the assumption at the top of the prototype.
|
||||
@@ -19,7 +19,7 @@ The two branches produce very different artifacts — getting this wrong wastes
|
||||
## Rules that apply to both
|
||||
|
||||
1. **Throwaway from day one, and clearly marked as such.** Locate the prototype code close to where it will actually be used (next to the module or page it's prototyping for) so context is obvious — but name it so a casual reader can see it's a prototype, not production. For throwaway UI routes, obey whatever routing convention the project already uses; don't invent a new top-level structure.
|
||||
2. **One command to run.** Whatever the project's existing task runner supports — `pnpm <name>`, `python <path>`, `bun <path>`, etc. The user must be able to start it without thinking.
|
||||
2. **Trivial to run.** A UI prototype starts from one command in the project's task runner — `pnpm <name>`, `python <path>`, `bun <path>`, etc. A logic demo is a single HTML file the user double-clicks. Either way, no thinking required to start it.
|
||||
3. **No persistence by default.** State lives in memory. Persistence is the thing the prototype is _checking_, not something it should depend on. If the question explicitly involves a database, hit a scratch DB or a local file with a clear "PROTOTYPE — wipe me" name.
|
||||
4. **Skip the polish.** No tests, no error handling beyond what makes the prototype _runnable_, no abstractions. The point is to learn something fast.
|
||||
5. **Surface the state.** After every action (logic) or on every variant switch (UI), print or render the full relevant state so the user can see what changed.
|
||||
|
||||
@@ -37,7 +37,7 @@ Lead each section with the recommended answer so the user can accept it in a wor
|
||||
|
||||
**Section A — Issue tracker.**
|
||||
|
||||
> Explainer: The "issue tracker" is where issues live for this repo. Skills like `to-tickets`, `triage`, `to-spec`, and `qa` read from and write to it — they need to know whether to call `gh issue create`, write a markdown file under `.scratch/`, or follow some other workflow you describe. Pick the place you actually track work for this repo.
|
||||
> Explainer: The "issue tracker" is where issues live for this repo. Skills like `to-tickets`, `triage`, and `to-spec` read from and write to it — they need to know whether to call `gh issue create`, write a markdown file under `.scratch/`, or follow some other workflow you describe. Pick the place you actually track work for this repo.
|
||||
|
||||
Default posture: these skills were designed for GitHub. If a `git remote` points at GitHub, propose that. If a `git remote` points at GitLab (`gitlab.com` or a self-hosted host), propose GitLab. Otherwise (or if the user prefers), offer:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Issue tracker: GitHub
|
||||
|
||||
Issues and PRDs for this repo live as GitHub issues. Use the `gh` CLI for all operations.
|
||||
Issues and specs for this repo live as GitHub issues. Use the `gh` CLI for all operations.
|
||||
|
||||
## Conventions
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Issue tracker: GitLab
|
||||
|
||||
Issues and PRDs for this repo live as GitLab issues. Use the [`glab`](https://gitlab.com/gitlab-org/cli) CLI for all operations.
|
||||
Issues and specs for this repo live as GitLab issues. Use the [`glab`](https://gitlab.com/gitlab-org/cli) CLI for all operations.
|
||||
|
||||
## Conventions
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Issue tracker: Local Markdown
|
||||
|
||||
Issues and specs (you may know a spec as a PRD) for this repo live as markdown files in `.scratch/`.
|
||||
Issues and specs for this repo live as markdown files in `.scratch/`.
|
||||
|
||||
## Conventions
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ A **seam** is the public boundary you test at: the interface where you observe b
|
||||
|
||||
Ask: "What's the public interface, and which seams should we test?"
|
||||
|
||||
When the shape of that interface is itself in question — how deep the module is, where the seam belongs, what the interface should expose — use the `/codebase-design` skill for the vocabulary. It is the shared source of the module, interface, depth, seam, adapter, leverage and locality terms, and it is a reference to consult, not a session to run.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- **Implementation-coupled** — mocks internal collaborators, tests private methods, or verifies through a side channel (querying the database instead of using the interface). The tell: the test breaks when you refactor but behavior hasn't changed.
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
---
|
||||
name: to-issues
|
||||
description: Break a plan, spec, or PRD into independently-grabbable issues on the project issue tracker using tracer-bullet vertical slices.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# To Issues
|
||||
|
||||
Break a plan into independently-grabbable issues using vertical slices (tracer bullets).
|
||||
|
||||
The issue tracker and triage label vocabulary should have been provided to you — run `/setup-matt-pocock-skills` if not.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Gather context
|
||||
|
||||
Work from whatever is already in the conversation context. If the user passes an issue reference (issue number, URL, or path) as an argument, fetch it from the issue tracker and read its full body and comments.
|
||||
|
||||
### 2. Explore the codebase (optional)
|
||||
|
||||
If you have not already explored the codebase, do so to understand the current state of the code. Issue titles and descriptions should use the project's domain glossary vocabulary, and respect ADRs in the area you're touching.
|
||||
|
||||
Look for opportunities to prefactor the code to make the implementation easier. "Make the change easy, then make the easy change."
|
||||
|
||||
### 3. Draft vertical slices
|
||||
|
||||
Break the plan into **tracer bullet** issues. Each issue is a thin vertical slice that cuts through ALL integration layers end-to-end, NOT a horizontal slice of one layer.
|
||||
|
||||
<vertical-slice-rules>
|
||||
|
||||
- Each slice delivers a narrow but COMPLETE path through every layer (schema, API, UI, tests)
|
||||
- A completed slice is demoable or verifiable on its own
|
||||
- Any prefactoring should be done first
|
||||
|
||||
</vertical-slice-rules>
|
||||
|
||||
### 4. Quiz the user
|
||||
|
||||
Present the proposed breakdown as a numbered list. For each slice, show:
|
||||
|
||||
- **Title**: short descriptive name
|
||||
- **Blocked by**: which other slices (if any) must complete first
|
||||
- **User stories covered**: which user stories this addresses (if the source material has them)
|
||||
|
||||
Ask the user:
|
||||
|
||||
- Does the granularity feel right? (too coarse / too fine)
|
||||
- Are the dependency relationships correct?
|
||||
- Should any slices be merged or split further?
|
||||
|
||||
Iterate until the user approves the breakdown.
|
||||
|
||||
### 5. Publish the issues to the issue tracker
|
||||
|
||||
For each approved slice, publish a new issue to the issue tracker. Use the issue body template below. These issues are considered ready for AFK agents, so publish them with the correct triage label unless instructed otherwise.
|
||||
|
||||
Publish issues in dependency order (blockers first) so you can reference real issue identifiers in the "Blocked by" field.
|
||||
|
||||
<issue-template>
|
||||
## Parent
|
||||
|
||||
A reference to the parent issue on the issue tracker (if the source was an existing issue, otherwise omit this section).
|
||||
|
||||
## What to build
|
||||
|
||||
A concise description of this vertical slice. Describe the end-to-end behavior, not layer-by-layer implementation.
|
||||
|
||||
Avoid specific file paths or code snippets — they go stale fast. Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it here and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Criterion 1
|
||||
- [ ] Criterion 2
|
||||
- [ ] Criterion 3
|
||||
|
||||
## Blocked by
|
||||
|
||||
- A reference to the blocking ticket (if any)
|
||||
|
||||
Or "None - can start immediately" if no blockers.
|
||||
|
||||
</issue-template>
|
||||
|
||||
Do NOT close or modify any parent issue.
|
||||
@@ -1,75 +0,0 @@
|
||||
---
|
||||
name: to-prd
|
||||
description: Turn the current conversation into a PRD and publish it to the project issue tracker — no interview, just synthesis of what you've already discussed.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
This skill takes the current conversation context and codebase understanding and produces a PRD. Do NOT interview the user — just synthesize what you already know.
|
||||
|
||||
The issue tracker and triage label vocabulary should have been provided to you — run `/setup-matt-pocock-skills` if not.
|
||||
|
||||
## Process
|
||||
|
||||
1. Explore the repo to understand the current state of the codebase, if you haven't already. Use the project's domain glossary vocabulary throughout the PRD, and respect any ADRs in the area you're touching.
|
||||
|
||||
2. Sketch out the seams at which you're going to test the feature. Existing seams should be preferred to new ones. Use the highest seam possible. If new seams are needed, propose them at the highest point you can. The fewer seams across the codebase, the better - the ideal number is one.
|
||||
|
||||
Check with the user that these seams match their expectations.
|
||||
|
||||
3. Write the PRD using the template below, then publish it to the project issue tracker. Apply the `ready-for-agent` triage label - no need for additional triage.
|
||||
|
||||
<prd-template>
|
||||
|
||||
## Problem Statement
|
||||
|
||||
The problem that the user is facing, from the user's perspective.
|
||||
|
||||
## Solution
|
||||
|
||||
The solution to the problem, from the user's perspective.
|
||||
|
||||
## User Stories
|
||||
|
||||
A LONG, numbered list of user stories. Each user story should be in the format of:
|
||||
|
||||
1. As an <actor>, I want a <feature>, so that <benefit>
|
||||
|
||||
<user-story-example>
|
||||
1. As a mobile bank customer, I want to see balance on my accounts, so that I can make better informed decisions about my spending
|
||||
</user-story-example>
|
||||
|
||||
This list of user stories should be extremely extensive and cover all aspects of the feature.
|
||||
|
||||
## Implementation Decisions
|
||||
|
||||
A list of implementation decisions that were made. This can include:
|
||||
|
||||
- The modules that will be built/modified
|
||||
- The interfaces of those modules that will be modified
|
||||
- Technical clarifications from the developer
|
||||
- Architectural decisions
|
||||
- Schema changes
|
||||
- API contracts
|
||||
- Specific interactions
|
||||
|
||||
Do NOT include specific file paths or code snippets. They may end up being outdated very quickly.
|
||||
|
||||
Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it within the relevant decision and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
|
||||
|
||||
## Testing Decisions
|
||||
|
||||
A list of testing decisions that were made. Include:
|
||||
|
||||
- A description of what makes a good test (only test external behavior, not implementation details)
|
||||
- Which modules will be tested
|
||||
- Prior art for the tests (i.e. similar types of tests in the codebase)
|
||||
|
||||
## Out of Scope
|
||||
|
||||
A description of the things that are out of scope for this PRD.
|
||||
|
||||
## Further Notes
|
||||
|
||||
Any further notes about the feature.
|
||||
|
||||
</prd-template>
|
||||
53
.agents/skills/to-questionnaire/SKILL.md
Normal file
53
.agents/skills/to-questionnaire/SKILL.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: to-questionnaire
|
||||
description: Turn a decision you can't fully answer into a questionnaire for someone else to fill in.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Turn something the user can't answer alone into a **questionnaire** — a Markdown document they hand to one person to fill in async, or fill out together over a meeting. The recipient holds knowledge the user lacks; the questionnaire pulls it out of them.
|
||||
|
||||
**Grill the send, not the subject.** Interview the user only about the _send_, which they can always answer: who it goes to, and what they need back. The questions in the document then target the **gap** between what the recipient knows and what the user needs.
|
||||
|
||||
1. **Who is it going to?** Ask, in one exchange, the recipient's role, expertise, and relationship to the user. This fixes the questionnaire's tone and how much context it must carry. Done when you know who the recipient is and what they know that the user doesn't.
|
||||
|
||||
2. **What do you need back?** Ask, in one exchange, the specific decisions or facts the user can't resolve alone and needs from this person. Done when you have a concrete list of what the user must walk away able to do or decide.
|
||||
|
||||
3. **Write the questionnaire.** Draft questions aimed at the gap from steps 1–2, following the Document structure below. Write it to `to-questionnaire-<slug>.md` in the current directory (slug from the topic) and report the path. Done when the file exists and every item the user named in step 2 is covered by a question.
|
||||
|
||||
## Document structure
|
||||
|
||||
Frame the document as a **discovery questionnaire**: the user lacks context, the recipient holds it. Order questions most-important-first — async means you may only get one pass — and group them under `##` headings by theme once there are more than a handful. Write it using the template below.
|
||||
|
||||
<questionnaire-template>
|
||||
|
||||
# <Questionnaire title>
|
||||
|
||||
**Purpose:** why this questionnaire exists and the decision riding on it.
|
||||
|
||||
**From:** <the user> — **To:** <the recipient> — **How your answers will be used:** <where they go>
|
||||
|
||||
## Context
|
||||
|
||||
One paragraph orienting a recipient who wasn't in the user's head. Enough to answer well, not a page.
|
||||
|
||||
## How to answer
|
||||
|
||||
Deadline and rough effort. Partial answers and "I don't know" are useful — flag anything you're unsure of rather than skipping it.
|
||||
|
||||
## <Theme heading>
|
||||
|
||||
One `##` section per theme. Under each, its questions, most-important-first. Every question is one idea — never compound — with an answer stub directly beneath, and a one-line _why this matters_ only where the question could be misread or invite a throwaway answer.
|
||||
|
||||
<question-example>
|
||||
### What load is the system expected to handle at launch?
|
||||
|
||||
_Why this matters: it decides whether we provision for burst traffic now or defer it._
|
||||
|
||||
>
|
||||
</question-example>
|
||||
|
||||
## Anything else?
|
||||
|
||||
A closing catch-all: anything we didn't ask that we should know?
|
||||
|
||||
</questionnaire-template>
|
||||
5
.agents/skills/to-questionnaire/agents/openai.yaml
Normal file
5
.agents/skills/to-questionnaire/agents/openai.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "To Questionnaire"
|
||||
short_description: "Front-load questions into a doc for someone to answer"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -4,7 +4,7 @@ description: Turn the current conversation into a spec and publish it to the pro
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
This skill takes the current conversation context and codebase understanding and produces a spec (you may know this document as a PRD). Do NOT interview the user — just synthesize what you already know.
|
||||
This skill takes the current conversation context and codebase understanding and produces a spec. Do NOT interview the user — just synthesize what you already know.
|
||||
|
||||
The issue tracker and triage label vocabulary should have been provided to you — run `/setup-matt-pocock-skills` if not.
|
||||
|
||||
|
||||
@@ -103,5 +103,3 @@ The end-to-end behaviour this ticket makes work, from the user's perspective —
|
||||
</issue-template>
|
||||
|
||||
In either form, avoid specific file paths or code snippets — they go stale fast. Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it and note briefly that it came from a prototype. Trim to the decision-rich parts — not a working demo, just the important bits.
|
||||
|
||||
Work the frontier one ticket at a time with `/implement`, clearing context between tickets.
|
||||
|
||||
@@ -73,7 +73,7 @@ Show counts and a one-line summary per item. Let the maintainer pick.
|
||||
|
||||
3. **Verify the claim.** Before any grilling, check that the claim holds up. For a bug, reproduce it from the reporter's steps. For a PR, confirm the diff does what it claims — check it out, run the relevant tests or commands. Report what happened: confirmed (with code path), failed, or insufficient detail (a strong `needs-info` signal). A confirmed verification makes a much stronger agent brief.
|
||||
|
||||
4. **Grill (if needed).** If the request needs fleshing out, run the `/grilling` and `/domain-modeling` skills together — grill it into shape one question at a time, sharpening domain terms and updating `CONTEXT.md`/ADRs inline as decisions land.
|
||||
4. **Grill (if needed).** If the request needs fleshing out, run the `/grilling` and `/domain-modeling` skills together — grill it into shape a round of questions at a time, sharpening domain terms and updating `CONTEXT.md`/ADRs inline as decisions land.
|
||||
|
||||
5. **Apply the outcome:**
|
||||
- `ready-for-agent` — post an agent brief comment ([AGENT-BRIEF.md](AGENT-BRIEF.md)).
|
||||
|
||||
7
.agents/skills/wait-what/SKILL.md
Normal file
7
.agents/skills/wait-what/SKILL.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
name: wait-what
|
||||
description: Stop. That last message did not land — re-pitch it.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Wait — I don't understand where you've got to here. Re-pitch that: give me a little bit of context, talk in ASD-STE100 Simplified Technical English, and use the ubiquitous language from `CONTEXT.md`.
|
||||
5
.agents/skills/wait-what/agents/openai.yaml
Normal file
5
.agents/skills/wait-what/agents/openai.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
interface:
|
||||
display_name: "Wait What"
|
||||
short_description: "Re-pitch that — simpler, with the context I'm missing"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -14,7 +14,7 @@ Wayfinder is **planning** by default: each ticket resolves a decision, and the m
|
||||
|
||||
## Refer by name
|
||||
|
||||
Every map and ticket is an issue, so it has a **name** — its title. In everything the human reads — narration, the map's Decisions-so-far — refer to it by that name, never by a bare id, number, or slug. A wall of `#42, #43, #44` is illegible; names read at a glance. The id and URL don't vanish — a name wraps its link — but they ride *inside* the name, never stand in for it.
|
||||
Every map and ticket is an issue, so it has a **name** — its title. In everything the human reads — narration, the map's Decisions-so-far — refer to it by that name, never by a bare id, number, or slug. A wall of `#42, #43, #44` is illegible; names read at a glance. The id and URL don't vanish — a name wraps its link — but they ride _inside_ the name, never stand in for it.
|
||||
|
||||
## The Map
|
||||
|
||||
@@ -72,12 +72,12 @@ The answer isn't part of the body — it's recorded on resolution (see [Work thr
|
||||
|
||||
## Ticket Types
|
||||
|
||||
Every ticket is either **HITL** — human in the loop, worked *with* a human who speaks for themselves — or **AFK**, driven by the agent alone. A HITL ticket only resolves through that live exchange; the agent never stands in for the human's side of it (a grilling agent that answers its own questions has broken this).
|
||||
Every ticket is either **HITL** — human in the loop, worked _with_ a human who speaks for themselves — or **AFK**, driven by the agent alone. A HITL ticket only resolves through that live exchange; the agent never stands in for the human's side of it (a grilling agent that answers its own questions has broken this).
|
||||
|
||||
- **Research** (AFK): Reading documentation, third-party APIs, or local resources like knowledge bases to surface a fact a decision waits on. Resolved by a `/research` **subagent**. Use when knowledge outside the current working directory is required.
|
||||
- **Prototype** (HITL): Raise the fidelity of the discussion by making a cheap, rough, concrete artifact to react to — an outline, a rough take, a stub, or UI/logic code via the /prototype skill. Links the prototype as an asset. Use when "how should it look" or "how should it behave" is the key question.
|
||||
- **Grilling** (HITL): Conversation via the /grilling and /domain-modeling skills, one question at a time. The default case.
|
||||
- **Task** (HITL or AFK): Manual work that must happen before a *decision* can be made — nothing to decide, prototype, or research, but the discussion is blocked until it's done. Signing up for a service so its API can be judged, provisioning access, moving data so its shape can be seen. This is the one type that *does* rather than decides — and it earns its place by unblocking a decision, not by delivering the destination. The agent drives it alone where it can (AFK); otherwise it hands the human a precise checklist (HITL). Resolved when the work is done; the answer records what was done and any resulting facts (credentials location, new URLs, row counts) later tickets depend on.
|
||||
- **Grilling** (HITL): Conversation. The default case. Always invoke the /grilling and /domain-modeling skills.
|
||||
- **Task** (HITL or AFK): Manual work that must happen before a _decision_ can be made — nothing to decide, prototype, or research, but the discussion is blocked until it's done. Signing up for a service so its API can be judged, provisioning access, moving data so its shape can be seen. This is the one type that _does_ rather than decides — and it earns its place by unblocking a decision, not by delivering the destination. The agent drives it alone where it can (AFK); otherwise it hands the human a precise checklist (HITL). Resolved when the work is done; the answer records what was done and any resulting facts (credentials location, new URLs, row counts) later tickets depend on.
|
||||
|
||||
## Fog of war
|
||||
|
||||
|
||||
44
.agents/skills/wizard/SKILL.md
Normal file
44
.agents/skills/wizard/SKILL.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
name: wizard
|
||||
description: Generate an interactive bash wizard that walks a human through steps only they can perform. Use when provisioning infrastructure, setting up credentials or CI secrets, walking an unfamiliar third-party dashboard, or running a one-off migration or cutover. Don't invoke this for steps the agent can perform itself.
|
||||
---
|
||||
|
||||
# Wizard
|
||||
|
||||
A **wizard** is a bash script that walks a human, step by step, through a manual procedure that's tedious to do by hand and tedious to re-explain to an AI every time. It opens each URL, says exactly what to click and copy, captures the values, writes them where they belong (`.env`, GitHub secrets), confirms at every stage, and shows how many stages are left. It might configure third-party services, run a one-off migration, or move the project from one state to another.
|
||||
|
||||
The delightful UX is already solved by [template.sh](template.sh) — stage-by-stage progress, confirmation gates, cross-platform URL opening (including WSL), hidden secret entry, idempotent `.env` upserts, `gh secret`/`gh variable` writes, and a closing summary. **Your job is only to scope the procedure and author its stages.** The library above the `STAGES` marker is identical in every wizard; that consistency is the point — never hand-edit it.
|
||||
|
||||
A wizard is ephemeral by default — built for one run, saved to a scratch or `scripts/` path, deleted when the job's done. Commit it only when the user wants a repeatable setup path that should live in the repo.
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Scope the procedure
|
||||
|
||||
Work out every manual step the human must take and every value that gets captured along the way. Read the repo first — don't ask cold:
|
||||
|
||||
- For setup: `.env`, `.env.example`, `.env.*`, `README`, `docker-compose*`, framework config, and `.github/workflows/*` (every `secrets.*` / `vars.*` reference is a value the wizard must produce).
|
||||
- For a migration or transition: the current state, the target state, and the irreversible actions between them.
|
||||
|
||||
Then show the user the ordered list of stages and the values each produces, and confirm — they may add, drop, or reorder.
|
||||
|
||||
**Done when:** every stage is named in order, and for each captured value you know (a) where the human gets it, (b) where it's written (`.env`, a GitHub secret, both, or nowhere — some stages are pure actions), and (c) whether it's secret (hidden entry) or public.
|
||||
|
||||
### 2. Map each stage's journey
|
||||
|
||||
For each stage, write the precise path a human follows: which URL to open, what to do there, where a value is shown, which variable it fills — e.g. "Dashboard → Developers → API keys → Reveal test key → copy". Where you don't actually know the current UI or the exact command, say so and ask the user or check the docs — never invent steps that may not exist.
|
||||
|
||||
**Done when:** every stage traces to concrete instructions a stranger could follow.
|
||||
|
||||
### 3. Author the wizard
|
||||
|
||||
Copy `template.sh` to the target path. Replace the example stage with one `stage` per step, in dependency order. Use the library helpers — `stage`, `say`/`step`, `open_url`, `ask`/`ask_secret`, `write_env`, `set_secret`/`set_var`, `pause`/`confirm` — and set `TOTAL_STAGES` to the number of stages you wrote.
|
||||
|
||||
Hold the bar the template sets: open the URL before asking for its value, use `ask_secret` for anything secret, `write_env` every persisted value, `set_secret` only the values CI actually needs, and `confirm` before any irreversible action. Each `stage` clears the screen so only the current step is visible — keep a stage to one focused task so nothing the human needs scrolls away. Don't touch the library above the marker.
|
||||
|
||||
### 4. Verify and hand off
|
||||
|
||||
- `bash -n <script>`; run `shellcheck` if available.
|
||||
- `chmod +x <script>`.
|
||||
- Don't run it end-to-end yourself — it opens browsers and blocks on human input. Trace it statically instead: every value from step 1 is captured and lands where step 1 said, and every `set_secret` name exactly matches a `secrets.*` reference in CI.
|
||||
- Tell the user how to run it. If it's a repeatable setup path, commit it and link it from the README so the next person runs the script instead of asking an AI.
|
||||
3
.agents/skills/wizard/agents/openai.yaml
Normal file
3
.agents/skills/wizard/agents/openai.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Wizard"
|
||||
short_description: "Generate an interactive setup wizard"
|
||||
204
.agents/skills/wizard/template.sh
Normal file
204
.agents/skills/wizard/template.sh
Normal file
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# A wizard — walks a human through a manual procedure step by step.
|
||||
# Generated by the /wizard skill.
|
||||
#
|
||||
# Everything above the "STAGES" marker is the wizard library: do not hand-edit
|
||||
# it. Author the per-step stages below the marker.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# Wizard library — delightful, consistent UX. Identical across every wizard.
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
if [[ -t 1 ]] && command -v tput >/dev/null 2>&1 && [[ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]]; then
|
||||
BOLD=$(tput bold); DIM=$(tput dim); RESET=$(tput sgr0)
|
||||
BLUE=$(tput setaf 4); GREEN=$(tput setaf 2); YELLOW=$(tput setaf 3); RED=$(tput setaf 1)
|
||||
else
|
||||
BOLD=""; DIM=""; RESET=""; BLUE=""; GREEN=""; YELLOW=""; RED=""
|
||||
fi
|
||||
|
||||
# Author sets this at the top of the stages section.
|
||||
TOTAL_STAGES=0
|
||||
|
||||
_STAGE_INDEX=0
|
||||
ENV_FILE="${ENV_FILE:-.env}"
|
||||
WRITTEN_ENV=() # KEYs written to ENV_FILE this run
|
||||
WRITTEN_SECRET=() # secret NAMEs set this run
|
||||
SKIPPED=() # things we couldn't do (e.g. gh missing)
|
||||
|
||||
# _clear — wipe the terminal so only the current step is on screen. No-op when
|
||||
# output isn't a terminal, so piped logs stay readable.
|
||||
_clear() {
|
||||
[[ -t 1 ]] || return 0
|
||||
if command -v tput >/dev/null 2>&1; then tput clear; else printf '\033[2J\033[3J\033[H'; fi
|
||||
}
|
||||
|
||||
# banner "Title" — opening frame: what this wizard does.
|
||||
banner() {
|
||||
_clear
|
||||
printf '\n%s%s %s%s\n' "$BOLD" "$BLUE" "$1" "$RESET"
|
||||
printf '%s %s stages%s\n\n' "$DIM" "$TOTAL_STAGES" "$RESET"
|
||||
printf '%s You drive the browser; this wizard tells you exactly what to do and\n' "$DIM"
|
||||
printf ' captures the values you copy back. Stop any time with Ctrl-C and re-run\n'
|
||||
printf ' later — it remembers values already saved.%s\n' "$RESET"
|
||||
pause "Ready to start?"
|
||||
}
|
||||
|
||||
# stage "Name" — clear the screen, then announce a stage and show progress.
|
||||
# Clearing keeps only the current step on screen.
|
||||
stage() {
|
||||
_clear
|
||||
_STAGE_INDEX=$((_STAGE_INDEX + 1))
|
||||
printf '\n%s%s▸ Stage %s/%s · %s%s\n' \
|
||||
"$BOLD" "$BLUE" "$_STAGE_INDEX" "$TOTAL_STAGES" "$1" "$RESET"
|
||||
}
|
||||
|
||||
# say "..." — a plain instruction line.
|
||||
say() { printf ' %s\n' "$1"; }
|
||||
# step "..." — a numbered-feeling action the human takes in the browser.
|
||||
step() { printf ' %s•%s %s\n' "$BLUE" "$RESET" "$1"; }
|
||||
note() { printf ' %s%s%s\n' "$DIM" "$1" "$RESET"; }
|
||||
warn() { printf ' %s⚠ %s%s\n' "$YELLOW" "$1" "$RESET"; }
|
||||
|
||||
# open_url URL — open in the human's browser, cross-platform incl. WSL.
|
||||
open_url() {
|
||||
local url="$1"
|
||||
printf ' %s↗ opening%s %s\n' "$GREEN" "$RESET" "$url"
|
||||
{ if command -v wslview >/dev/null 2>&1; then wslview "$url"
|
||||
elif command -v explorer.exe >/dev/null 2>&1; then explorer.exe "$url"
|
||||
elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$url"
|
||||
elif command -v open >/dev/null 2>&1; then open "$url"
|
||||
else warn "couldn't open a browser — visit it manually: $url"; fi
|
||||
} >/dev/null 2>&1 || warn "couldn't open a browser — visit it manually: $url"
|
||||
}
|
||||
|
||||
# pause "msg" — wait for the human to confirm they've done the manual part.
|
||||
pause() {
|
||||
printf ' %s%s%s ' "$DIM" "${1:-Press Enter to continue}" "$RESET"
|
||||
read -r _ || true
|
||||
}
|
||||
|
||||
# confirm "question" — y/N gate; returns success on yes.
|
||||
confirm() {
|
||||
local reply=""
|
||||
printf ' %s? %s [y/N] ' "$YELLOW" "$1"
|
||||
read -r reply || true
|
||||
[[ "$reply" =~ ^[Yy] ]]
|
||||
}
|
||||
|
||||
# _existing KEY — current value of KEY in ENV_FILE, if any.
|
||||
_existing() {
|
||||
[[ -f "$ENV_FILE" ]] || return 1
|
||||
local line; line=$(grep -E "^${1}=" "$ENV_FILE" | tail -n1) || return 1
|
||||
printf '%s' "${line#*=}"
|
||||
}
|
||||
|
||||
# ask KEY "Prompt" — read a value into $KEY. Offers the existing .env value as
|
||||
# a default on re-runs (Enter keeps it). Visible input (non-secret).
|
||||
ask() {
|
||||
local key="$1" prompt="$2" current input
|
||||
current=$(_existing "$key" || true)
|
||||
if [[ -n "$current" ]]; then
|
||||
printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
|
||||
else
|
||||
printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
|
||||
fi
|
||||
read -r input || true
|
||||
[[ -z "$input" && -n "$current" ]] && input="$current"
|
||||
printf -v "$key" '%s' "$input"
|
||||
}
|
||||
|
||||
# ask_secret KEY "Prompt" — like ask, but input is hidden.
|
||||
ask_secret() {
|
||||
local key="$1" prompt="$2" current input
|
||||
current=$(_existing "$key" || true)
|
||||
if [[ -n "$current" ]]; then
|
||||
printf ' %s%s%s %s[Enter keeps current]%s ' "$BOLD" "$prompt" "$RESET" "$DIM" "$RESET"
|
||||
else
|
||||
printf ' %s%s%s ' "$BOLD" "$prompt" "$RESET"
|
||||
fi
|
||||
read -rs input || true
|
||||
printf '\n'
|
||||
[[ -z "$input" && -n "$current" ]] && input="$current"
|
||||
printf -v "$key" '%s' "$input"
|
||||
}
|
||||
|
||||
# write_env KEY VALUE — upsert KEY=VALUE into ENV_FILE (creates it; replaces
|
||||
# any existing line). Idempotent.
|
||||
write_env() {
|
||||
local key="$1" value="$2" tmp
|
||||
touch "$ENV_FILE"
|
||||
tmp=$(mktemp)
|
||||
grep -vE "^${key}=" "$ENV_FILE" > "$tmp" || true
|
||||
printf '%s=%s\n' "$key" "$value" >> "$tmp"
|
||||
mv "$tmp" "$ENV_FILE"
|
||||
WRITTEN_ENV+=("$key")
|
||||
printf ' %s✓ wrote%s %s → %s\n' "$GREEN" "$RESET" "$key" "$ENV_FILE"
|
||||
}
|
||||
|
||||
# set_secret NAME VALUE — set a GitHub Actions repo secret via gh. Falls back
|
||||
# to a warning (and records it) if gh is unavailable or unauthenticated.
|
||||
set_secret() {
|
||||
local name="$1" value="$2"
|
||||
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
|
||||
if printf '%s' "$value" | gh secret set "$name" >/dev/null 2>&1; then
|
||||
WRITTEN_SECRET+=("$name")
|
||||
printf ' %s✓ set%s GitHub secret %s\n' "$GREEN" "$RESET" "$name"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
SKIPPED+=("GitHub secret $name (set it manually: gh secret set $name)")
|
||||
warn "skipped GitHub secret $name — gh not ready; set it later"
|
||||
}
|
||||
|
||||
# set_var NAME VALUE — set a GitHub Actions repo variable (non-secret).
|
||||
set_var() {
|
||||
local name="$1" value="$2"
|
||||
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
|
||||
if gh variable set "$name" --body "$value" >/dev/null 2>&1; then
|
||||
printf ' %s✓ set%s GitHub variable %s\n' "$GREEN" "$RESET" "$name"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
SKIPPED+=("GitHub variable $name")
|
||||
warn "skipped GitHub variable $name — gh not ready; set it later"
|
||||
}
|
||||
|
||||
# finish — clear, then a closing summary of everything configured.
|
||||
finish() {
|
||||
_clear
|
||||
printf '\n%s%s ✓ Setup complete%s\n' "$BOLD" "$GREEN" "$RESET"
|
||||
(( ${#WRITTEN_ENV[@]} )) && note "wrote ${#WRITTEN_ENV[@]} value(s) to $ENV_FILE: ${WRITTEN_ENV[*]}"
|
||||
(( ${#WRITTEN_SECRET[@]} )) && note "set ${#WRITTEN_SECRET[@]} GitHub secret(s): ${WRITTEN_SECRET[*]}"
|
||||
if (( ${#SKIPPED[@]} )); then
|
||||
printf '\n'; warn "still to do by hand:"
|
||||
for s in "${SKIPPED[@]}"; do note " - $s"; done
|
||||
fi
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# STAGES — author this section. One stage() per step the human takes.
|
||||
# Replace the example below. Set TOTAL_STAGES to match the stages you write.
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
TOTAL_STAGES=1
|
||||
|
||||
banner "Stripe setup"
|
||||
|
||||
# ── Example stage: replace with your real steps ───────────────────────────
|
||||
stage "Stripe — API keys"
|
||||
say "We'll grab your Stripe test keys and store them for local dev + CI."
|
||||
open_url "https://dashboard.stripe.com/test/apikeys"
|
||||
step "On the API keys page, copy the Publishable key (starts pk_test_)."
|
||||
ask STRIPE_PUBLISHABLE_KEY "Paste the publishable key:"
|
||||
step "Click 'Reveal test key' on the Secret key row, then copy it."
|
||||
ask_secret STRIPE_SECRET_KEY "Paste the secret key:"
|
||||
write_env STRIPE_PUBLISHABLE_KEY "$STRIPE_PUBLISHABLE_KEY"
|
||||
write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"
|
||||
set_secret STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY" # CI needs this one
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
finish
|
||||
@@ -1,117 +0,0 @@
|
||||
---
|
||||
name: write-a-skill
|
||||
description: Create new agent skills with proper structure, progressive disclosure, and bundled resources. Use when user wants to create, write, or build a new skill.
|
||||
---
|
||||
|
||||
# Writing Skills
|
||||
|
||||
## Process
|
||||
|
||||
1. **Gather requirements** - ask user about:
|
||||
- What task/domain does the skill cover?
|
||||
- What specific use cases should it handle?
|
||||
- Does it need executable scripts or just instructions?
|
||||
- Any reference materials to include?
|
||||
|
||||
2. **Draft the skill** - create:
|
||||
- SKILL.md with concise instructions
|
||||
- Additional reference files if content exceeds 500 lines
|
||||
- Utility scripts if deterministic operations needed
|
||||
|
||||
3. **Review with user** - present draft and ask:
|
||||
- Does this cover your use cases?
|
||||
- Anything missing or unclear?
|
||||
- Should any section be more/less detailed?
|
||||
|
||||
## Skill Structure
|
||||
|
||||
```
|
||||
skill-name/
|
||||
├── SKILL.md # Main instructions (required)
|
||||
├── REFERENCE.md # Detailed docs (if needed)
|
||||
├── EXAMPLES.md # Usage examples (if needed)
|
||||
└── scripts/ # Utility scripts (if needed)
|
||||
└── helper.js
|
||||
```
|
||||
|
||||
## SKILL.md Template
|
||||
|
||||
```md
|
||||
---
|
||||
name: skill-name
|
||||
description: Brief description of capability. Use when [specific triggers].
|
||||
---
|
||||
|
||||
# Skill Name
|
||||
|
||||
## Quick start
|
||||
|
||||
[Minimal working example]
|
||||
|
||||
## Workflows
|
||||
|
||||
[Step-by-step processes with checklists for complex tasks]
|
||||
|
||||
## Advanced features
|
||||
|
||||
[Link to separate files: See [REFERENCE.md](REFERENCE.md)]
|
||||
```
|
||||
|
||||
## Description Requirements
|
||||
|
||||
The description is **the only thing your agent sees** when deciding which skill to load. It's surfaced in the system prompt alongside all other installed skills. Your agent reads these descriptions and picks the relevant skill based on the user's request.
|
||||
|
||||
**Goal**: Give your agent just enough info to know:
|
||||
|
||||
1. What capability this skill provides
|
||||
2. When/why to trigger it (specific keywords, contexts, file types)
|
||||
|
||||
**Format**:
|
||||
|
||||
- Max 1024 chars
|
||||
- Write in third person
|
||||
- First sentence: what it does
|
||||
- Second sentence: "Use when [specific triggers]"
|
||||
|
||||
**Good example**:
|
||||
|
||||
```
|
||||
Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when user mentions PDFs, forms, or document extraction.
|
||||
```
|
||||
|
||||
**Bad example**:
|
||||
|
||||
```
|
||||
Helps with documents.
|
||||
```
|
||||
|
||||
The bad example gives your agent no way to distinguish this from other document skills.
|
||||
|
||||
## When to Add Scripts
|
||||
|
||||
Add utility scripts when:
|
||||
|
||||
- Operation is deterministic (validation, formatting)
|
||||
- Same code would be generated repeatedly
|
||||
- Errors need explicit handling
|
||||
|
||||
Scripts save tokens and improve reliability vs generated code.
|
||||
|
||||
## When to Split Files
|
||||
|
||||
Split into separate files when:
|
||||
|
||||
- SKILL.md exceeds 100 lines
|
||||
- Content has distinct domains (finance vs sales schemas)
|
||||
- Advanced features are rarely needed
|
||||
|
||||
## Review Checklist
|
||||
|
||||
After drafting, verify:
|
||||
|
||||
- [ ] Description includes triggers ("Use when...")
|
||||
- [ ] SKILL.md under 100 lines
|
||||
- [ ] No time-sensitive info
|
||||
- [ ] Consistent terminology
|
||||
- [ ] Concrete examples included
|
||||
- [ ] References one level deep
|
||||
22
.agents/skills/writing-for-agents/SKILL-MECHANICS.md
Normal file
22
.agents/skills/writing-for-agents/SKILL-MECHANICS.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Skill mechanics
|
||||
|
||||
The skill-specific branch of [`writing-for-agents`](SKILL.md): what changes when the document is a skill — frontmatter, the invocation choice, and router skills. Everything else about writing it is the universal reference in `SKILL.md`.
|
||||
|
||||
## Invocation
|
||||
|
||||
Two choices, trading the two loads:
|
||||
|
||||
- A **model-invoked** skill keeps a `description`, so the agent can fire it autonomously — and other skills can reach it. You can still type its name: model-invocation always _includes_ user reach; a description only ever adds agent discovery, never removes the human's. The description is the skill's top-level context pointer, forced to stay loaded at all times — permanent context load in exchange for discoverability. A model-invoked skill whose content is all reference is also one home for shared reference: another skill can invoke it, so reference needed by several skills lives in one place. Mechanics: omit `disable-model-invocation`, and write a model-facing description carrying the trigger branches (the pointer-writing rules in `SKILL.md` apply in full).
|
||||
- A **user-invoked** skill strips the description from the agent's reach: only the human typing its name can invoke it, and no other skill can. Zero context load, but it spends cognitive load — you are the index that must remember it exists. Mechanics: set `disable-model-invocation: true`; the `description` becomes human-facing — a one-line summary, trigger lists stripped.
|
||||
|
||||
Pick model-invocation only when the agent must reach the skill on its own, or another skill must. If it only ever fires by hand, make it user-invoked and pay no context load.
|
||||
|
||||
Shared reference that two user-invoked skills both need can live in neither — with no descriptions, neither can fire the other. Push it to a plain file outside the skill system: external reference any skill can point at.
|
||||
|
||||
## Splitting by invocation
|
||||
|
||||
The invocation cut of splitting (the sequence cut lives in `SKILL.md`): split off a model-invoked skill when you have a distinct leading word that should trigger it on its own — a trigger word you actually use in your prompts — or another skill must reach it. You pay context load for the new always-loaded description, so that independent reach has to be worth it.
|
||||
|
||||
## Router skills
|
||||
|
||||
When user-invoked skills multiply past what you can remember, that piled-up cognitive load is cured by a **router skill**: one user-invoked skill that names the others and when to reach for each, so the human has one skill to remember instead of many. It can only hint, never fire them: user-invoked skills have no description, so nothing but the human can reach them.
|
||||
81
.agents/skills/writing-for-agents/SKILL.md
Normal file
81
.agents/skills/writing-for-agents/SKILL.md
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
name: writing-for-agents
|
||||
description: Writing documents for agents. Use when creating or editing skills, or modifying AGENTS.md or CLAUDE.md.
|
||||
---
|
||||
|
||||
Reference for writing any document an agent consumes — a skill, an `AGENTS.md` / `CLAUDE.md`, a doc reached by a pointer. The packaging differs; the writing does not: the same levers make each one predictable — the agent taking the same _process_ every run, not producing the same output.
|
||||
|
||||
When the document you're writing is a skill, read [`SKILL-MECHANICS.md`](SKILL-MECHANICS.md) for frontmatter, invocation choice, and router skills.
|
||||
|
||||
## Context pointers
|
||||
|
||||
A **context pointer** is a reference held in the agent's context that names some out-of-context material and encodes the condition for reaching it. A skill's description is one; a line in `AGENTS.md` naming a doc is the same object. The pointer's _wording_, not its target, decides when the agent reaches the material — and how reliably. A must-have target behind a weakly worded pointer is a variance bug: sharpen the wording first, and inline the material only if sharpening fails.
|
||||
|
||||
A pointer does two jobs — state what the material is, and list the **branches** that should trigger reaching it (a branch is a distinct case the document handles, so different runs take different paths through it). Every word of an always-loaded pointer costs on every turn, so it earns even harder pruning than the body:
|
||||
|
||||
- **Front-load the leading word** — the pointer is where it does its triggering work.
|
||||
- **One trigger per branch.** Synonyms that rename a single branch are one branch written twice; collapse them and keep only genuinely distinct branches.
|
||||
- **Cut identity the body already carries.**
|
||||
|
||||
## The two loads
|
||||
|
||||
Every document and pointer you add spends one of two budgets:
|
||||
|
||||
- **Context load** — the cost of always-loaded material on the agent's window: an `AGENTS.md` line, a skill description, anything sitting in context every turn, spending tokens and attention whether or not it fires.
|
||||
- **Cognitive load** — the cost on the human: which documents exist and when to reach for each. The human is the index. Not a cost to minimise — it is the price of human agency; spend it where human judgement matters, remove it where it does not.
|
||||
|
||||
Material reached only through a pointer escapes context load at the price of the pointer's own line; material with no pointer at all rides entirely on cognitive load.
|
||||
|
||||
## Information hierarchy
|
||||
|
||||
A document is built from two content types — **steps** (the ordered actions the agent performs) and **reference** (definitions, rules, facts consulted on demand) — that mix freely: all steps (a recipe), all reference (a review's rules, this skill), or both. The core decision is where each piece sits on the **information hierarchy**, a ladder ranked by how immediately the agent needs the material:
|
||||
|
||||
1. **In-file step** — the primary tier: what the agent does, in order.
|
||||
2. **In-file reference** — consulted on demand. Often a legitimately flat peer-set (every rule of a review on one rung) — a fine arrangement, not a smell.
|
||||
3. **Disclosed reference** — pushed out into a separate file, reached by a context pointer, loaded only when the pointer fires. Spans a sibling file in the same folder through fully external reference that lives anywhere and any document can point at.
|
||||
|
||||
Push too little down and the top bloats; push too much and you hide material the agent actually needs. That tension is the whole decision.
|
||||
|
||||
**Progressive disclosure** is the move down the ladder — out of the main file and behind a pointer — so the top stays legible. Not primarily a token optimisation: it is how the hierarchy is protected. Branching is the cleanest disclosure test: inline what every branch needs, and push behind a pointer what only some branches reach. When a document has steps, in-file reference that should be disclosed buries them and turns attending to them into a coin-flip — a variance lever, not just a legibility one.
|
||||
|
||||
**Co-location** is the within-file companion: where the ladder decides _how far down_ a piece sits, co-location decides _what sits beside it_ once there. Keep a concept's definition, rules, and caveats under one heading rather than scattered, so reading one part brings its neighbours with it. The test: the document should read like documentation written for the agent — grouped material reads that way; scattered material does not. (Distinct from duplication: that repeats one meaning in two places; scattering fragments one meaning across many.)
|
||||
|
||||
**Sprawl** is the failure mode here: a document simply too long, even when every line is live and unique. Attention thins across the excess, and every extra line is one more to keep relevant. The cure is the ladder: disclose reference behind pointers, and split by branch or sequence so each path carries only what it needs.
|
||||
|
||||
## Steps and completion criteria
|
||||
|
||||
Every step ends on a **completion criterion** — the condition that tells the agent the work is done. Two properties make it a lever:
|
||||
|
||||
- **Clarity** — can the agent tell done from not-done? A vague bound ("understanding reached") invites **premature completion**: ending the step before it is genuinely done, attention slipping to _being done_. The visible steps still ahead — the **post-completion steps** — supply the pull; the criterion's clarity is the resistance. Defend in order: **sharpen the bound first** (local and cheap); only if it is irreducibly fuzzy _and_ you observe the rush, hide the later steps by splitting the sequence — and hiding only works across a real context boundary (a hand-off or a subagent dispatch; an inline call leaves the later steps in context and clears nothing).
|
||||
- **Demand** — how much it requires. "Every modified model accounted for" forces thorough work where "produce a change list" does not. Demand drives **legwork** — the digging the agent does within the work, latent in the wording rather than written as its own step — and it is not step-bound: "every rule applied" binds a body of flat reference just as "every step done" binds a sequence, which is how an all-reference document still carries an exhaustiveness bar.
|
||||
|
||||
The strongest criteria are both checkable and exhaustive.
|
||||
|
||||
## When to split
|
||||
|
||||
Splitting one document into two spends one of the two loads, so split only when the cut earns it:
|
||||
|
||||
- **By sequence** — split a run of steps where the post-completion steps tempt the agent to rush the one in front of it. Keeping them out of view drives more legwork on the current task. Beware the reverse: merging sequences exposes each step's later steps to what follows, inviting premature completion.
|
||||
- **By invocation** — skill-specific: see [`SKILL-MECHANICS.md`](SKILL-MECHANICS.md).
|
||||
|
||||
## Leading words
|
||||
|
||||
A **leading word** is a compact concept already living in the model's pretraining that the agent thinks with while running the document (_lesson_, _fog of war_, _tracer bullets_). Repeated as a token, never as a sentence, it accumulates a distributed definition and anchors a whole region of behaviour in the fewest tokens, by recruiting priors the model already holds. Coining your own works if you define it clearly, but a made-up word recruits no priors — you pay in definition tokens what a pretrained word gives free; reach for an existing word first.
|
||||
|
||||
It anchors twice. In the body, _execution_: the agent reaches for the same behaviour every time the word appears, and inside flat reference it focuses attention on a class of thing to look for. In a pointer, _invocation_: when the same word lives in your prompts, your docs, and your codebase, the agent links that shared language to the material and reaches it more reliably.
|
||||
|
||||
Hunt for opportunities to refactor with leading words. A triad spelled out at three sites, a pointer spending a sentence to gesture at one idea — each is a passage begging to collapse into a single token:
|
||||
|
||||
- "fast, deterministic, low-overhead" → _tight_ (a _tight_ loop).
|
||||
- "a loop you believe in" → _red_ — a fuzzy gate becomes a binary observable state (the loop goes _red_ on the bug, or it doesn't).
|
||||
|
||||
You win twice: fewer tokens, and a sharper hook for the agent to hang its thinking on. Assume every document is carrying restatements that leading words retire — go find them.
|
||||
|
||||
**Negation** is the failure mode beside this lever: steering by prohibition drags the forbidden behaviour into context and makes it _more_ available, not less. _Don't think of an elephant_, and the elephant is all there is; the negation is a weak modifier the strongly-activated concept overruns, so the ban half-reads as an instruction to do the thing. Prompt the **positive** — state the target behaviour ("write one-line comments") so the banned one is never spoken. A prohibition earns its place only as a hard guardrail you cannot phrase positively; even then, pair it with the positive target so attention lands on what to do.
|
||||
|
||||
## Pruning
|
||||
|
||||
- Keep each meaning in a **single source of truth**: one authoritative place, so changing the behaviour is a one-place edit. **Duplication** — the same meaning in more than one place — costs maintenance and tokens, and inflates a meaning's prominence on the ladder past its real rank. (The accidental inverse of a leading word, which repeats a token on purpose, never the meaning.)
|
||||
- The **environment** is a source of truth too — `package.json` scripts, config files, the directory layout, `--help` output — and a document that restates it is a **cache**: a copy of a lookup, earning its load only when the lookup is expensive. Cache what the agent cannot find by looking: the unwritten convention, the reason behind a choice, the gotcha no config confesses. Leave the one-file, one-command lookups to the environment, where they cannot go stale.
|
||||
- Check every line for **relevance**: does it still bear on what the document does? A line loses relevance by never bearing on the task (mere exposition, or a branch that should be disclosed) or by going stale as the behaviour or world it describes changes. Shorter documents are easier to keep relevant. Without a pruning discipline the default fate is **sediment**: stale layers that settle because adding feels safe and removing feels risky, until you must core down through them to find what is still live.
|
||||
- Hunt **no-ops** sentence by sentence: an instruction the model already obeys by default pays load to say nothing. The test — does it change behaviour versus the default? — is model-relative, not reader-relative: two people disagreeing about a no-op disagree about the default, and settle it by running the document, not by debate. When a sentence fails, delete the whole sentence rather than trim words from it. The test also grades leading words: a word too weak to beat the default (_be thorough_ when the agent is already thorough-ish) is a no-op, and the fix is a stronger word (_relentless_), not a different technique.
|
||||
3
.agents/skills/writing-for-agents/agents/openai.yaml
Normal file
3
.agents/skills/writing-for-agents/agents/openai.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
interface:
|
||||
display_name: "Writing for Agents"
|
||||
short_description: "Write documents agents consume"
|
||||
@@ -1,201 +0,0 @@
|
||||
# Glossary — Building Great Skills
|
||||
|
||||
The domain model for what makes a skill great. A skill exists to wrangle determinism out of a stochastic system; the root virtue is **Predictability**, and every term below is a lever on it. This is the disclosed reference for [`writing-great-skills`](SKILL.md).
|
||||
|
||||
The terms are grouped by axis: **Invocation** (how a skill is reached), **Information Hierarchy** (how its content is arranged), **Steering** (how the agent's runtime behaviour is shaped), and **Pruning** (how it is kept lean). Each **failure mode** lives beside the lever that cures it, tagged _failure mode_.
|
||||
|
||||
**Bold terms** in any definition are themselves defined in this glossary; find them by their heading.
|
||||
|
||||
## Predictability
|
||||
|
||||
The degree to which a skill makes the agent behave the same _way_ on every run — the same process, not the same output (a brainstorming skill should _predictably_ diverge; its tokens vary, its behaviour doesn't). The root virtue every other term serves — cost and maintainability are symptoms of it, not rivals.
|
||||
|
||||
_Avoid_: consistency, reliability, robustness, output-determinism
|
||||
|
||||
## Invocation
|
||||
|
||||
How a skill is reached — and the two loads you pay for the choice.
|
||||
|
||||
### Model-Invoked
|
||||
|
||||
A skill that keeps its **description** field, so the agent can see it and fire it autonomously — and the human can still type its name, so model-invocation always _includes_ user reach. There is no model-only state: a description only ever _adds_ agent discovery, never removes the human's. Pays a permanent **context load** on every turn in exchange for that discoverability. Reachable by other skills, because the description that makes it agent-discoverable makes it invocable. A model-invoked skill whose content is all **reference** is also one home for shared reference: another skill can invoke it, so reference needed by several skills lives in one place. Pick model-invocation only when the agent must reach the skill on its own; if it never fires except by hand, drop the description and pay no context load.
|
||||
|
||||
_Avoid_: ability, tool, capability
|
||||
|
||||
### User-Invoked
|
||||
|
||||
A skill with its **description** stripped — invisible to the agent and reachable only by the human typing its name (user-_only_, where **model-invoked** is user-_and-agent_). Trades agent-discoverability for zero **context load**. Because it has no description, nothing but the human can reach it: no other skill can fire it.
|
||||
|
||||
_Avoid_: procedure, workflow, command
|
||||
|
||||
### Description
|
||||
|
||||
The skill's machine-readable trigger, and the one **context pointer** a **model-invoked** skill is forced to keep loaded at all times. Its mere presence _is_ the invocation axis: keep it and the skill is model-invoked (and reachable by other skills); delete it and the skill is **user-invoked**, reachable only by the human. The source of a model-invoked skill's **context load**.
|
||||
|
||||
_Avoid_: frontmatter, summary
|
||||
|
||||
### Context Pointer
|
||||
|
||||
A reference held in the agent's context that names some out-of-context material and encodes the condition for reaching it. The **description** is the top-level context pointer (context window → skill); pointers to disclosed files are the same object one level down. Its wording, not the target, decides _when_ the agent reaches — and _how reliably_. A must-have target behind a weakly worded pointer is a variance bug: fix the wording first, and inline the material only if sharpening fails.
|
||||
|
||||
_Avoid_: link, reference, import
|
||||
|
||||
### Context Load
|
||||
|
||||
The cost a **model-invoked** skill imposes on the agent's context window — its **description**, always loaded, spending both tokens and attention. What **user-invoked** skills escape by having no description, and the brake on splitting into more model-invoked skills.
|
||||
|
||||
_Avoid_: token cost, context bloat
|
||||
|
||||
### Cognitive Load
|
||||
|
||||
The cost a **user-invoked** skill imposes on the human — what they must hold in their head: which skills exist and when to reach for each (the human is the index). What **model-invocation** removes by being agent-discoverable, and the brake on splitting into more user-invoked skills. Not a cost to minimise: it is the price of human agency, the reason some skills stay user-invoked. Spend it where human judgement matters; remove it where it does not.
|
||||
|
||||
_Avoid_: human index, burden, overhead
|
||||
|
||||
### Router Skill
|
||||
|
||||
A **user-invoked** skill whose job is to point at your other user-invoked skills — naming each and when to reach for it — so the human has one skill to remember instead of many. It can only hint, never fire them: user-invoked skills have no **description**, so nothing but the human can reach them. The cure for **cognitive load** when user-invoked skills multiply.
|
||||
|
||||
_Avoid_: dispatcher, menu, registry, index, router procedure
|
||||
|
||||
### Granularity
|
||||
|
||||
How finely you divide skills. Finer division spends one of the two loads: more **model-invoked** skills spend **context load** (more descriptions crowding the window and competing for attention); more **user-invoked** skills spend **cognitive load** (more for the human to remember and reach for). Two cuts guide the division. By **invocation**, split off a model-invoked skill where you have a distinct **leading word** to trigger it — a trigger word you actually use in your prompts. By **sequence**, split a run of **steps** where a step's **post-completion steps** need hiding, since isolating it in its own context clears what follows. Beware the reverse: merging sequences exposes each step's post-completion steps to what follows, inviting premature completion.
|
||||
|
||||
_Avoid_: chunking, modularity
|
||||
|
||||
## Information Hierarchy
|
||||
|
||||
How a skill's content is arranged, and how far down the ladder each piece sits.
|
||||
|
||||
### Information Hierarchy
|
||||
|
||||
A skill's content ranked by how immediately the agent needs it — a single ladder, produced by two cuts: in-file or behind a pointer, and step or reference. The rungs:
|
||||
|
||||
- **Steps** — in-file, primary
|
||||
- **Reference**, in-file — secondary
|
||||
- **Reference**, disclosed — behind a **context pointer**
|
||||
|
||||
A skill with no **steps** uses just the bottom two rungs — often a legitimately flat peer-set (e.g. every rule of a review on one rung), which is a fine arrangement, not a smell. The hierarchy is independent of invocation: a skill can be model- or user-invoked whether it is all steps, all reference, or both. When a skill has steps, in-file reference that should be disclosed buries them and turns attending to them into a coin-flip — a variance lever, not just a legibility one. Keep the top of the ladder legible; push down it whatever you can.
|
||||
|
||||
_Avoid_: structure, organization, layout
|
||||
|
||||
### Steps
|
||||
|
||||
The ordered actions the agent performs — when a skill has them, the primary tier of its content, and the part that earns its place in SKILL.md. Not every skill has steps: a skill can be all steps (`tdd`), all **reference** (a review), or both, independent of invocation. Every step ends on a **completion criterion**, clear or vague.
|
||||
|
||||
_Avoid_: workflow, instructions, choreography
|
||||
|
||||
### Reference
|
||||
|
||||
Material the agent refers to on demand — definitions, facts, parameters, examples, conditional instructions. When a skill has **steps** it is secondary to them; when a skill has none it is the entire content; or it lives outside any skill entirely — see **External Reference**. Reached via **context pointers**, and the prime candidate for **progressive disclosure**.
|
||||
|
||||
_Avoid_: supporting material, docs, background
|
||||
|
||||
### External Reference
|
||||
|
||||
**Reference** that lives outside the skill system — a plain file, no **description**, no **steps**, not invocable — that any skill can point at. The home for shared reference that needn't fire on its own, and the only shared home two **user-invoked** skills can use, since neither has a description and so neither can fire the other.
|
||||
|
||||
_Avoid_: doc, resource, knowledge base
|
||||
|
||||
### Progressive Disclosure
|
||||
|
||||
Moving **reference** down the ladder — out of SKILL.md and behind a **context pointer** — so the top stays legible. Not primarily a token optimisation; it is how the **information hierarchy** is protected. Licensed by **branching**: disclose what only some branches need, inline what every path needs, and if a pointer fires unreliably on must-have material, sharpen its wording, and pull it back inline only if that fails.
|
||||
|
||||
_Avoid_: lazy loading, chunking
|
||||
|
||||
### Co-location
|
||||
|
||||
Keeping the material an agent needs at once in one place — a concept's definition, rules, and caveats under a single heading, not scattered across the file — so reading one part brings its neighbours with it. The within-file companion to the **Information Hierarchy**: the hierarchy ranks _how far down_ a piece sits; co-location decides _what sits beside it_ once there. There is no formula for the right format of a body of **reference**; the test is that a skill should read like documentation written for the agent, and grouped material reads that way where scattered material does not. Distinct from **Duplication**: that repeats one meaning in two places, where scattering fragments a single meaning across many.
|
||||
|
||||
_Avoid_: grouping, clustering, cohesion
|
||||
|
||||
### Sprawl
|
||||
|
||||
_Failure mode._ A skill that is simply too long — too many lines in SKILL.md — independent of whether they are stale or repeated. Even an all-live, all-unique skill can sprawl. It costs readability (the agent wades through more before it can act, and attention thins across the excess), maintainability (every extra line is one more to keep **relevant**), and tokens. The cure is the **information hierarchy**: push **reference** down behind **context pointers**, and split by **branch** or sequence so each path carries only what it needs. Distinct from **sediment** (length from stale accumulation) and **duplication** (length from repeated meaning) — sprawl is length itself, whatever its cause.
|
||||
|
||||
_Avoid_: bloat, length, size, verbosity
|
||||
|
||||
## Steering
|
||||
|
||||
The levers that shape the agent's runtime behaviour toward **Predictability**.
|
||||
|
||||
### Branch
|
||||
|
||||
A distinct way a skill can be invoked — a case the skill handles — so different runs take different paths through it. A skill with many steps may carry many branches; a linear one has none.
|
||||
|
||||
_Avoid_: path, case, fork
|
||||
|
||||
### Leading Word
|
||||
|
||||
A compact concept — also called a _Leitwort_ — already living in the model's pretraining, that the agent thinks with while running the skill. It encodes a behavioural principle in the fewest possible tokens by invoking priors the model already holds (e.g. _lesson_, _proximal zone of development_, _fog of war_, _tracer bullets_). Repeated as a token, never as a sentence, it accumulates a distributed definition across the skill and anchors a whole region of behaviour. Coining your own works if you define it clearly, but a made-up word recruits no priors — you pay in definition tokens what a pretrained word gives free. Reach for an existing word first.
|
||||
|
||||
A leading word serves **predictability** twice. In the body it anchors **execution** — the agent reaches for the same behaviour every time the concept appears, and inside flat reference it focuses attention on a class of thing to look for, recruiting the right checks each run. In the **description** it anchors **invocation** — and not only within the skill: when the same word lives in your prompts, your docs, and your codebase, the agent links that shared language to the skill and fires it more reliably. Word a description with the leading words you actually use when you want the skill.
|
||||
|
||||
_Avoid_: keyword, term, motif
|
||||
|
||||
### Completion Criterion
|
||||
|
||||
The condition that tells the agent a unit of work is done — the target it judges against. Two properties make it a lever, not just a quality. Its **clarity** (can the agent tell done from not-done?) resists **premature completion** — a vague bound ("understanding reached") lets the agent declare done and slip to the next step; this axis needs _steps_ to bite, since premature completion is a between-steps failure. Its **demand** (how much it requires) sets **legwork** — "every modified model accounted for" forces thorough work where "produce a change list" does not — and this axis is _not_ step-bound: it can bind a body of flat reference too, which is how a skill with no steps still carries an exhaustiveness bar ("every rule applied"). The strongest criteria are both checkable and exhaustive.
|
||||
|
||||
_Avoid_: done condition, exit condition, stopping rule
|
||||
|
||||
### Legwork
|
||||
|
||||
The work an agent does behind the scenes within a single step — reading files, exploring the codebase, making changes, digging up what it needs rather than offloading to the user. It lives below the step structure: never written as its own step, latent in the wording, controlled by the agent rather than the skill. The within-step counterpart to **post-completion steps**' across-step pull. Raised by a **leading word** (_comprehensive_, _thorough_) or a **completion criterion** that demands the work be exhaustive — including the demand axis applied to flat reference, which is what drives a skill of flat reference to cover all its rungs. Goes thin either when that demand is missing or when **premature completion** cuts the step short.
|
||||
|
||||
_Avoid_: scope, effort, diligence, coverage
|
||||
|
||||
### Post-Completion Steps
|
||||
|
||||
The **steps** that follow the current step. Visible, they pull the agent forward into **premature completion** — the more it sees, the stronger the tug; the defence is to hide them by splitting the sequence of steps into two.
|
||||
|
||||
_Avoid_: horizon, fog of war, lookahead
|
||||
|
||||
### Premature Completion
|
||||
|
||||
_Failure mode._ Ending the current step before it is genuinely done, because the agent's attention slips to being done rather than to the work. A between-steps failure: it needs **steps** to occur — a skill with no steps that quits early isn't premature completion but thin **legwork** under an unmet demand. A tug-of-war between two forces: visible **post-completion steps** (the pull forward) and the **completion criterion**'s clarity (the resistance — a sharp, checkable bar holds; a vague one gives way). Fuzziness is the necessary condition: a sharp bound resists the pull no matter how many later steps are visible, so a step that never rushes needs no defending. Two levers hold a step that does, but reach for them in order: **sharpen the bound first** — it is local and cheap. Only when the criterion is irreducibly fuzzy _and_ you actually observe the rush do you **hide the later steps** — and hiding only works across a real context boundary (a user-invoked hand-off or a subagent dispatch; an inline model-invoked call leaves the later steps in context and clears nothing). One cause of thin legwork, but distinct from it: legwork can be thin even when a step runs to full completion.
|
||||
|
||||
_Avoid_: premature closure, the rush, rushing, shortcutting
|
||||
|
||||
### Negation
|
||||
|
||||
_Failure mode._ Steering by prohibition — telling the agent what _not_ to do — which drags the forbidden behaviour into context and makes it _more_ available, not less. _Don't think of an elephant_, and the elephant is all there is; _never write verbose comments_, and verbosity is the pattern the agent has just read. The negation is a weak modifier the strongly-activated concept overruns, so the ban half-reads as an instruction to do the thing. Its **leading word** is the _elephant_: whatever a prohibition names into the frame. Cure: prompt the **positive** — describe the target behaviour ("write one-line comments") so the banned one is never spoken. A prohibition earns its place only as a hard guardrail on a behaviour you cannot phrase positively; even then, pair it with the positive target so attention lands on what to do.
|
||||
|
||||
_Avoid_: ironic rebound, don't-prompting, the pink elephant
|
||||
|
||||
## Pruning
|
||||
|
||||
Keeping a skill lean — each remedy paired with the failure it cures.
|
||||
|
||||
### Single Source of Truth
|
||||
|
||||
The desired state where each meaning lives in exactly one authoritative place, so a change to the skill's behaviour is a change in one place. **Duplication** is its violation.
|
||||
|
||||
_Avoid_: home, canonical location
|
||||
|
||||
### Duplication
|
||||
|
||||
_Failure mode._ The same meaning given more than one **single source of truth**. It costs maintenance (change one place, you must change the others), costs tokens, and inflates prominence — repeating a meaning weights it on the ladder past its real rank. The accidental inverse of a **leading word**, which raises attention on purpose by repeating a token, never the meaning.
|
||||
|
||||
_Avoid_: repetition, redundancy
|
||||
|
||||
### Relevance
|
||||
|
||||
Whether a line still bears on what the skill does — the lens for what to keep. A line loses relevance either by never bearing on the task (mere exposition, or a **branch** that should be disclosed) or by going stale: drifting out of date as the behaviour or world it describes changes. Shorter skills are easier to keep relevant, because each line is cheaper to check. Distinct from **no-op**: relevance asks whether a line bears on the task, not whether it changes behaviour.
|
||||
|
||||
_Avoid_: load-bearing, staleness, freshness
|
||||
|
||||
### Sediment
|
||||
|
||||
_Failure mode._ Layers of old content that settle in a skill and are never cleared, because adding feels safe and removing feels risky — so stale and irrelevant lines accumulate and you must core down through them to find what is still live. The default fate of any skill without a pruning discipline; the slow erosion of **relevance**, as opposed to **duplication**'s repeated meaning.
|
||||
|
||||
_Avoid_: accretion, bloat, cruft, rot
|
||||
|
||||
### No-Op
|
||||
|
||||
_Failure mode._ An instruction that changes nothing because the model already does it by default — you pay load to tell the agent what it would do anyway. The test: does a line change behaviour versus the default? A line can be perfectly **relevant** and still be a no-op. The same priors that make a **leading word** free make a no-op worthless.
|
||||
|
||||
A leading word is a _technique_; No-Op is a _verdict_ on a line — and they cross. A leading word too weak to beat the default is a no-op (_be thorough_ when the agent is already thorough-ish), and the fix is a stronger word that passes the verdict (_relentless_), not a different technique. So the No-Op test — does it change behaviour versus the default? — is also how you grade whether a leading word is earning its repetitions. This is model-relative, not reader-relative: two people disagreeing over whether a line is a no-op disagree about the default, and settle it by running the skill, not by debate.
|
||||
|
||||
_Avoid_: redundant instruction, restating the obvious, belaboring
|
||||
@@ -1,83 +0,0 @@
|
||||
---
|
||||
name: writing-great-skills
|
||||
description: Reference for writing and editing skills well — the vocabulary and principles that make a skill predictable.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
A skill exists to wrangle determinism out of a stochastic system. **Predictability** — the agent taking the same _process_ every run, not producing the same output — is the root virtue; every lever below serves it.
|
||||
|
||||
**Bold terms** are defined in [`GLOSSARY.md`](GLOSSARY.md); look them up there for the full meaning.
|
||||
|
||||
## Invocation
|
||||
|
||||
Two choices, trading different costs:
|
||||
|
||||
- A **model-invoked** skill keeps a **description**, so the agent can fire it autonomously _and_ other skills can reach it (you can still type its name too). It contributes to **context load** — the description sits in the window every turn. Mechanics: omit `disable-model-invocation`, and write a model-facing description with rich trigger phrasing ("Use when the user wants…, mentions…").
|
||||
- A **user-invoked** skill strips the description from the agent's reach: only you, typing its name, can invoke it — and no other skill can. Zero context load, but it spends **cognitive load**: _you_ are the index that must remember it exists. Mechanics: set `disable-model-invocation: true`; the `description` becomes human-facing — a one-line summary, trigger lists stripped.
|
||||
|
||||
Pick model-invocation only when the agent must reach the skill on its own, or another skill must. If it only ever fires by hand, make it user-invoked and pay no context load.
|
||||
|
||||
When user-invoked skills multiply past what you can remember, that piled-up cognitive load is cured by a **router skill**: one user-invoked skill that names the others and when to reach for each.
|
||||
|
||||
## Writing the description
|
||||
|
||||
A model-invoked **description** does two jobs — state what the skill is, and list the **branches** that should trigger it. Every word increases **context load**, so a description earns even harder pruning than the body:
|
||||
|
||||
- **Front-load the skill's leading word** — the description is where it does its invocation work.
|
||||
- **One trigger per branch.** Synonyms that rename a single branch are **duplication** — "build features using TDD … asks for test-first development" is one branch written twice. Collapse them; keep only genuinely distinct branches.
|
||||
- **Cut identity that's already in the body.** Keep the description to triggers, plus any "when another skill needs…" reach clause.
|
||||
|
||||
## Information hierarchy
|
||||
|
||||
A skill is built from two content types — **steps** and **reference** — that mix freely: a skill can be all steps, all reference, or both. The core decision is which to use and where each sits on the **information hierarchy**, a ladder ranked by how immediately the agent needs the material:
|
||||
|
||||
1. **In-skill step** — an ordered action in `SKILL.md`, the primary tier: what the agent does, in order. Each step ends on a **completion criterion**, the condition that tells the agent the work is done. Make it _checkable_ (can the agent tell done from not-done?) and, where it matters, _exhaustive_ ("every modified model accounted for", not "produce a change list") — a vague criterion invites **premature completion**.
|
||||
2. **In-skill reference** — a definition, rule, or fact in `SKILL.md`, consulted on demand. Often a legitimately flat peer-set (every rule of a review on one rung) — a fine arrangement, not a smell. _This skill is all reference._
|
||||
3. **External reference** — reference pushed out of `SKILL.md` into a separate file, reached by a **context pointer**, loaded only when the pointer fires. (Spans _disclosed_ reference — a sibling file like `GLOSSARY.md`, still part of the skill — through fully **external reference** that lives outside the skill system and any skill can point at.)
|
||||
|
||||
A demanding completion criterion drives thorough **legwork** — the digging the agent does within the work — whether the skill has steps or not, since "every rule applied" binds flat reference just as "every step done" binds a sequence.
|
||||
|
||||
Push too little down and the top bloats; push too much and you hide material the agent actually needs. That tension is the whole decision.
|
||||
|
||||
**Progressive disclosure** is the move down the ladder — out of `SKILL.md` into a linked file — so the top stays legible. Mechanics: a linked `.md` file in the skill folder, named for what it holds (this skill discloses its full definitions to `GLOSSARY.md`). Some skills are used in more than one way, and each distinct way is a **branch** — different runs taking different paths through the skill. Branching is the cleanest disclosure test: inline what every branch needs, and push behind a pointer what only some branches reach. A **context pointer**'s _wording_, not its target, decides when and how reliably the agent reaches the material.
|
||||
|
||||
Where the ladder decides _how far down_ a piece sits, **co-location** decides _what sits beside it_ once there: keep a concept's definition, rules, and caveats under one heading rather than scattered, so reading one part brings its neighbours with it.
|
||||
|
||||
## When to split
|
||||
|
||||
**Granularity** is how finely you divide skills, and each cut spends one of the two loads, so split only when the cut earns it. Two cuts:
|
||||
|
||||
- **By invocation** — split off a **model-invoked** skill when you have a distinct **leading word** that should trigger it on its own, or another skill must reach it. You pay **context load** for the new always-loaded **description**, so that independent reach has to be worth it.
|
||||
- **By sequence** — split a run of **steps** when the steps still ahead (a step's **post-completion steps**) tempt the agent to rush the one in front of it (**premature completion**). Keeping them out of view encourages the agent to do more **legwork** on the current task.
|
||||
|
||||
## Pruning
|
||||
|
||||
Keep each meaning in a **single source of truth**: one authoritative place, so changing the behaviour is a one-place edit.
|
||||
|
||||
Check every line for **relevance**: does it still bear on what the skill does?
|
||||
|
||||
Then hunt **no-ops** sentence by sentence, not just line by line: run the no-op test on each sentence in isolation, and when one fails, delete the whole sentence rather than trim words from it. Be aggressive — most prose that fails should go, not be rewritten.
|
||||
|
||||
## Leading words
|
||||
|
||||
A **leading word** is a compact concept already living in the model's pretraining that the agent thinks with while running the skill (e.g. _lesson_, _fog of war_, _tracer bullets_). Repeated throughout the text (though not necessarily - a strong leading word might only be needed once), it accumulates a distributed definition and anchors a whole region of behaviour in the fewest tokens, by recruiting priors the model already holds.
|
||||
|
||||
It serves predictability twice. In the body it anchors _execution_: the agent reaches for the same behaviour every time the word appears. In the description it anchors _invocation_: when the same word lives in your prompts, docs, and code, the agent links that shared language to the skill and fires it more reliably.
|
||||
|
||||
Hunt for opportunities to refactor skills to use leading words. A triad spelled out at three sites (**duplication**), a description spending a sentence to gesture at one idea — each is a passage begging to **collapse** into a single token. Examples include:
|
||||
|
||||
- "fast, deterministic, low-overhead" -> _tight_ — one quality restated across a phase — into a single pretrained word (a _tight_ loop).
|
||||
- "a loop you believe in" -> _red_ — converts a fuzzy gate into a binary observable state (the loop goes _red_ on the bug, or it doesn't).
|
||||
|
||||
You win twice over: fewer tokens, _and_ a sharper hook for the agent to hang its thinking on. Assume every skill is carrying restatements that leading words retire — go find them.
|
||||
|
||||
## Failure modes
|
||||
|
||||
Use these to diagnose issues the user may be having with the skill.
|
||||
|
||||
- **Premature completion** — ending a step before it's genuinely done, attention slipping to _being done_. Defence, in order: sharpen the completion criterion first (cheap, local); only if it is irreducibly fuzzy _and_ you observe the rush, hide the post-completion steps by splitting (the sequence cut).
|
||||
- **Duplication** — the same meaning in more than one place. Costs maintenance and tokens, and inflates a meaning's prominence on the ladder past its real rank.
|
||||
- **Sediment** — stale layers that settle because adding feels safe and removing feels risky. The default fate of any skill without a pruning discipline.
|
||||
- **Sprawl** — a skill simply too long, even when every line is live and unique. Hurts readability and maintainability and wastes tokens. The cure is the ladder: disclose **reference** behind pointers, and split by **branch** or sequence so each path carries only what it needs.
|
||||
- **No-op** — a line the model already obeys by default, so you pay load to say nothing. The test: does it change behaviour versus the default? A weak leading word (_be thorough_ when the agent is already thorough-ish) is a no-op; the fix is a stronger word (_relentless_), not a different technique.
|
||||
- **Negation** — steering by prohibition backfires: _don't think of an elephant_ names the elephant and makes it more available, not less. Prompt the **positive** — state the target behaviour so the banned one is never spoken; keep a prohibition only as a hard guardrail you can't phrase positively, and even then pair it with what to do instead.
|
||||
@@ -1,5 +0,0 @@
|
||||
interface:
|
||||
display_name: "Writing Great Skills"
|
||||
short_description: "Principles for predictable skills"
|
||||
policy:
|
||||
allow_implicit_invocation: false
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
name: zoom-out
|
||||
description: Tell the agent to zoom out and give broader context or a higher-level perspective. Use when you're unfamiliar with a section of code or need to understand how it fits into the bigger picture.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
I don't know this area of code well. Go up a layer of abstraction. Give me a map of all the relevant modules and callers, using the project's domain glossary vocabulary.
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
name: "OPSX: Apply"
|
||||
description: Implement tasks from an OpenSpec change (Experimental)
|
||||
description: "Implement tasks from an OpenSpec change (Experimental)"
|
||||
allowed-tools: Bash(openspec:*)
|
||||
category: Workflow
|
||||
tags: [workflow, artifacts, experimental]
|
||||
category: "Workflow"
|
||||
tags: ["workflow", "artifacts", "experimental"]
|
||||
---
|
||||
|
||||
Implement tasks from an OpenSpec change.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: Optionally specify a change name (e.g., `/opsx:apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
@@ -19,7 +19,7 @@ Implement tasks from an OpenSpec change.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:apply <other>`).
|
||||
|
||||
@@ -39,16 +39,33 @@ Implement tasks from an OpenSpec change.
|
||||
```
|
||||
|
||||
This returns:
|
||||
- `contextFiles`: artifact ID -> array of concrete file paths (varies by schema)
|
||||
- `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)
|
||||
- Progress (total, complete, remaining)
|
||||
- Task list with status
|
||||
- Dynamic instruction based on current state
|
||||
- Optional `context`: current required project instruction input from the selected root
|
||||
- Optional `operationGuidance`: current advisory guidance for apply
|
||||
|
||||
**Handle states:**
|
||||
- If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx:continue`
|
||||
- If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx:continue` (if it is not installed, run `openspec status --change "<name>" --json` to see the next artifact and `openspec instructions <artifact-id> --change "<name>" --json` for how to create it)
|
||||
- If `state: "all_done"`: congratulate, suggest archive
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
Treat `context` as a required prompt-level input. Read and consider it, and
|
||||
apply relevant project facts, conventions, and constraints while implementing.
|
||||
Treat `operationGuidance` as optional additive advice. Read and consider every
|
||||
entry, and follow entries that are applicable and compatible with the built-in
|
||||
workflow.
|
||||
|
||||
Keep both fields separate from CLI-returned state, missing artifacts, tasks,
|
||||
progress, `contextFiles`, and the built-in `instruction`. They are not
|
||||
evidence of task completion, do not replace the built-in instruction, and do
|
||||
not permit bypassing a blocked state. If context conflicts with the built-in
|
||||
instruction, an explicit user choice, or a CLI-controlled value, report the
|
||||
conflict and preserve the controlling value. If guidance is inapplicable or
|
||||
conflicts with those controlling inputs, do not follow it and explain why.
|
||||
These are prompt-level behavior contracts, not enforceable checks.
|
||||
|
||||
4. **Read context files**
|
||||
|
||||
Read every file path listed under `contextFiles` from the apply instructions output.
|
||||
@@ -56,6 +73,9 @@ Implement tasks from an OpenSpec change.
|
||||
- **spec-driven**: proposal, specs, design, tasks
|
||||
- Other schemas: follow the contextFiles from CLI output
|
||||
|
||||
Do not copy `context` or `operationGuidance` verbatim into implementation
|
||||
files or planning artifacts unless the user separately asks for that content.
|
||||
|
||||
5. **Show current progress**
|
||||
|
||||
Display:
|
||||
@@ -147,6 +167,11 @@ What would you like to do?
|
||||
- Update task checkbox immediately after completing each task
|
||||
- Pause on errors, blockers, or unclear requirements - don't guess
|
||||
- Use contextFiles from CLI output, don't assume specific file names
|
||||
- Do not use context or operation guidance as proof that a task is complete
|
||||
- Apply relevant project context; report conflicts with controlling workflow inputs
|
||||
- Consider every guidance entry; explain any inapplicable or conflicting advice
|
||||
- Do not copy runtime context or operation guidance into implementation files or planning artifacts
|
||||
- Preserve CLI-controlled blocked/ready/all-done behavior and completion criteria
|
||||
|
||||
**Fluid Workflow Integration**
|
||||
|
||||
|
||||
@@ -1,27 +1,59 @@
|
||||
---
|
||||
name: "OPSX: Archive"
|
||||
description: Archive a completed change in the experimental workflow
|
||||
description: "Archive a completed change in the experimental workflow"
|
||||
allowed-tools: Bash(openspec:*)
|
||||
category: Workflow
|
||||
tags: [workflow, archive, experimental]
|
||||
category: "Workflow"
|
||||
tags: ["workflow", "archive", "experimental"]
|
||||
---
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve the full path from each delta spec when resolving its main spec.
|
||||
|
||||
**Input**: Optionally specify a change name after `/opsx:archive` (e.g., `/opsx:archive add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
1. **Select the change**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
Show only active changes (not already archived).
|
||||
When prompting, show only active changes (not already archived).
|
||||
Include the schema used for each change if available.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:archive <other>`).
|
||||
|
||||
**Load current archive inputs before the existing archive checks:**
|
||||
|
||||
After resolving the selected change and planning root, run:
|
||||
```bash
|
||||
openspec instructions archive --change "<name>" --json
|
||||
```
|
||||
Keep the same selected-root flags on this command. This lookup is advisory and
|
||||
optional: it only supplies extra prompt inputs, so it must never block archiving.
|
||||
If it exits non-zero or returns invalid JSON — for example on an older CLI that
|
||||
does not support this command yet — continue the archive workflow with no
|
||||
context and no operation guidance. Do not report an error and do not stop.
|
||||
|
||||
A successful response may omit both optional fields. Treat `context` as a
|
||||
required prompt-level input: read and consider it, and apply relevant project
|
||||
facts, conventions, and constraints. Treat `operationGuidance` as optional
|
||||
additive advice: read and consider every entry, and follow entries that are
|
||||
applicable and compatible with the built-in archive workflow.
|
||||
|
||||
Keep both fields separate from built-in steps, explicit user choices, resolved
|
||||
paths, CLI checks, and command contracts. If context conflicts with one of those
|
||||
controlling inputs, report the conflict and preserve the controlling value. If
|
||||
guidance is inapplicable or conflicts with a controlling input, do not follow it
|
||||
and explain why. Do not infer replacement paths, skipped prompts, or flags from
|
||||
either field, and do not copy their text verbatim into specs, change artifacts,
|
||||
or archive summaries unless the user separately asks for it. These are
|
||||
prompt-level behavior contracts, not enforceable checks.
|
||||
|
||||
2. **Check artifact completion status**
|
||||
|
||||
@@ -30,9 +62,9 @@ Archive a completed change in the experimental workflow.
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context
|
||||
- `artifacts`: List of artifacts with their status (`done` or other)
|
||||
- `artifacts`: List of artifacts with their status (`done`, `skipped`, or other)
|
||||
|
||||
**If any artifacts are not `done`:**
|
||||
**If any artifacts are neither `done` nor `skipped`** (skipped artifacts satisfy the requirement - the change declares skip_specs):
|
||||
- Display warning listing incomplete artifacts
|
||||
- Prompt user for confirmation to continue
|
||||
- Proceed if user confirms
|
||||
@@ -52,10 +84,13 @@ Archive a completed change in the experimental workflow.
|
||||
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.
|
||||
Use `artifactPaths.specs.existingOutputPaths` from status JSON as the only
|
||||
delta-spec source. If the `specs` entry is missing or
|
||||
`existingOutputPaths` is empty, proceed without a sync prompt and do not infer
|
||||
delta specs from other artifacts.
|
||||
|
||||
**If delta specs exist:**
|
||||
- Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`
|
||||
- Compare each delta spec with its corresponding main spec at `<planningHome.root>/openspec/specs/<capability-path>/spec.md` (use the store-aware `planningHome.root` from step 2, not a hardcoded repo path)
|
||||
- Determine what changes would be applied (adds, modifications, removals, renames)
|
||||
- Show a combined summary before prompting
|
||||
|
||||
@@ -63,7 +98,30 @@ Archive a completed change in the experimental workflow.
|
||||
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
||||
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
||||
|
||||
If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>"). Proceed to archive regardless of choice.
|
||||
Route on the answer:
|
||||
- "Cancel" — stop, do not archive
|
||||
- "Archive without syncing" or "Archive now" — proceed to archive
|
||||
- "Sync now" or "Sync anyway" — sync, then verify (below)
|
||||
- Anything else — ask again rather than archiving
|
||||
|
||||
Before a selected sync writes any main spec, run
|
||||
`openspec instructions specs --change "<name>" --json` once with the same
|
||||
selected-root flags. Require a zero exit status and valid artifact-instruction
|
||||
JSON. If the lookup fails or returns invalid JSON, report the error and stop
|
||||
before writing any main spec or moving the change. A valid response with omitted
|
||||
`rules` is the no-rules case. Apply returned `rules` only to the content and
|
||||
form of main specs produced by this merge; do not use them as archive guidance,
|
||||
change CLI behavior, or copy the rule text into any output file.
|
||||
|
||||
Then run the `/opsx:sync` workflow inline (agent-driven intelligent merge) for change '<name>', passing the delta spec analysis and the fetched specs-rule snapshot from above, and wait for it to finish. The inline sync must reuse that snapshot without fetching `specs` instructions again. Do not delegate it to a background task — step 5 would move `changeRoot` out from under a sync that is still reading it, leaving the change archived and the main specs never updated. If your agent can only run it by delegation, delegate synchronously and wait for the result.
|
||||
|
||||
Then re-run the comparison from the top of this step against every capability that has a delta spec in `artifactPaths.specs.existingOutputPaths` — not only the ones the sync reports it touched. A successful sync leaves nothing left to apply, so each capability must now read as already synced:
|
||||
- ADDED requirements present
|
||||
- MODIFIED requirements carrying the scenario and description changes named in the delta, with their other scenarios intact
|
||||
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving `## Requirements` empty), its main spec deleted rather than left empty; a spec the sync deliberately kept and reported is also a match
|
||||
- RENAMED requirements present under the new name and absent under the old one
|
||||
|
||||
If the sync failed, or any capability does not match, report what differs and stop — do not archive. Nothing has moved and `changeRoot` is intact, so the user can fix the mismatch or re-run the sync and start the archive again.
|
||||
|
||||
5. **Perform the archive**
|
||||
|
||||
@@ -72,14 +130,14 @@ Archive a completed change in the experimental workflow.
|
||||
mkdir -p "<planningHome.changesDir>/archive"
|
||||
```
|
||||
|
||||
Generate target name using current date: `YYYY-MM-DD-<change-name>`
|
||||
Generate the target name: use the change name as-is when it already starts with a `YYYY-MM-DD-` prefix; otherwise prepend the current date as `YYYY-MM-DD-<change-name>`. Never stack a second date (same rule as `openspec archive`).
|
||||
|
||||
**Check if target already exists:**
|
||||
- If yes: Fail with error, suggest renaming existing archive or using different date
|
||||
- If no: Move `changeRoot` to the archive directory
|
||||
|
||||
```bash
|
||||
mv "<changeRoot>" "<planningHome.changesDir>/archive/YYYY-MM-DD-<name>"
|
||||
mv "<changeRoot>" "<planningHome.changesDir>/archive/<target-name>"
|
||||
```
|
||||
|
||||
6. **Display summary**
|
||||
@@ -93,12 +151,12 @@ Archive a completed change in the experimental workflow.
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
```markdown
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/
|
||||
**Archived to:** the archive path derived from `planningHome.changesDir`/<target-name>/
|
||||
**Specs:** ✓ Synced to main specs
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
@@ -106,12 +164,12 @@ All artifacts complete. All tasks complete.
|
||||
|
||||
**Output On Success (No Delta Specs)**
|
||||
|
||||
```
|
||||
```markdown
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/
|
||||
**Archived to:** the archive path derived from `planningHome.changesDir`/<target-name>/
|
||||
**Specs:** No delta specs
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
@@ -119,12 +177,12 @@ All artifacts complete. All tasks complete.
|
||||
|
||||
**Output On Success With Warnings**
|
||||
|
||||
```
|
||||
```markdown
|
||||
## Archive Complete (with warnings)
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/
|
||||
**Archived to:** the archive path derived from `planningHome.changesDir`/<target-name>/
|
||||
**Specs:** Sync skipped (user chose to skip)
|
||||
|
||||
**Warnings:**
|
||||
@@ -137,11 +195,11 @@ Review the archive if this was not intentional.
|
||||
|
||||
**Output On Error (Archive Exists)**
|
||||
|
||||
```
|
||||
```markdown
|
||||
## Archive Failed
|
||||
|
||||
**Change:** <change-name>
|
||||
**Target:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/
|
||||
**Target:** the archive path derived from `planningHome.changesDir`/<target-name>/
|
||||
|
||||
Target archive directory already exists.
|
||||
|
||||
@@ -152,10 +210,16 @@ Target archive directory already exists.
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Always prompt for change selection if not provided
|
||||
- Announce the selected change; prompt for selection when it is ambiguous
|
||||
- Use artifact graph (openspec status --json) for completion checking
|
||||
- Don't block archive on warnings - just inform and confirm
|
||||
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
||||
- Show clear summary of what happened
|
||||
- If sync is requested, use the Skill tool to invoke `openspec-sync-specs` (agent-driven)
|
||||
- If sync is requested, run the `/opsx:sync` workflow inline (agent-driven)
|
||||
- Never archive while a spec sync is still in flight — run the sync inline and verify the main specs before moving `changeRoot`
|
||||
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
||||
- Apply relevant runtime context and report conflicts; operation guidance remains advisory
|
||||
- Consider every guidance entry and explain any inapplicable or conflicting advice
|
||||
- Existing CLI checks, resolved paths, prompts, and command contracts are unchanged
|
||||
- Artifact rules constrain only the specs being written and are never operation guidance
|
||||
- Never copy runtime context, operation guidance, or artifact-rule text verbatim into output files
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
name: "OPSX: Explore"
|
||||
description: "Enter explore mode - think through ideas, investigate problems, clarify requirements"
|
||||
allowed-tools: Bash(openspec:*)
|
||||
category: Workflow
|
||||
tags: [workflow, explore, experimental, thinking]
|
||||
category: "Workflow"
|
||||
tags: ["workflow", "explore", "experimental", "thinking"]
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing. For a new change, scaffold it first as described below.
|
||||
|
||||
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: The argument after `/opsx:explore` is whatever the user wants to think about. Could be:
|
||||
- A vague idea: "real-time collaboration"
|
||||
@@ -97,6 +97,12 @@ This tells you:
|
||||
- Their names, schemas, and status
|
||||
- What the user might be working on
|
||||
|
||||
Then read the project's own context from the resolved root - `<root.path>/openspec/config.yaml` (or `config.yml`). Use the `root.path` returned above, and skip this if neither file exists:
|
||||
- `context`: project background - tech stack, conventions, constraints
|
||||
- `rules`: keyed by artifact id - the entries for an artifact apply only when you write that artifact
|
||||
|
||||
Ground your thinking in these. They are constraints for you to follow, not content to reproduce: do NOT copy them into the conversation or into any artifact you create.
|
||||
|
||||
If the user mentioned a specific change name, read its artifacts for context.
|
||||
|
||||
### When no change exists
|
||||
@@ -106,6 +112,15 @@ Think freely. When insights crystallize, you might offer:
|
||||
- "This feels solid enough to start a change. Want me to create a proposal?"
|
||||
- Or keep exploring - no pressure to formalize
|
||||
|
||||
If the user asks you to capture the exploration as a new change, transition seamlessly into the requested capture:
|
||||
|
||||
1. Run `openspec new change "<name>"` (with `--store <id>` when applicable) before creating any artifacts. Never create a new change directory under `openspec/changes/` by hand; the CLI scaffold creates required metadata such as `.openspec.yaml`. Keep the selected `--store <id>` on every applicable follow-up `status` and `instructions` command.
|
||||
2. Run `openspec status --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store), then process the requested artifacts in dependency order. For each requested artifact that is `ready`, run `openspec instructions "<artifact-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store). Before creating a requested artifact, evaluate any condition in its own `instruction` against the explored change; record a deliberate skip instead when the condition does not apply. If a requested artifact is blocked by a direct prerequisite the user did not request, run `openspec instructions "<prerequisite-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) for that prerequisite whether it is `ready` or `blocked`. If its own `instruction` states a condition, evaluate that condition against the explored change and record a deliberate skip only when the condition does not apply. If the condition applies, or the prerequisite is not conditional, treat it as a normal prerequisite and ask before expanding the capture. Do not create an unrequested prerequisite unless the user approves.
|
||||
3. Follow the returned `template` and `instruction` fields. Read completed dependency files listed in `dependencies`, and apply `context` and `rules` as constraints without copying them into the artifact. If the instruction delegates creation to a specific skill or command, invoke it; otherwise write the artifact to `resolvedOutputPath`, using the instruction to choose a concrete path when it is a glob. Verify that the selected concrete output exists.
|
||||
4. After creating each artifact, re-run `openspec status --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) and continue until every requested artifact is `done`, `skipped`, or was deliberately skipped because its own `instruction` stated a condition that did not apply. Tell the user about a deliberate conditional skip, remember it, and do not reconsider it. Dependencies are enablers, not gates: if a requested artifact is still `blocked` only because you deliberately skipped a conditional prerequisite, run `openspec instructions "<artifact-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) despite the blocked status, then create it using step 3 only when those recorded conditional skips are its sole missing dependencies. If a requested artifact is blocked by a prerequisite the user did not ask to capture and cannot be conditionally skipped, explain that dependency and ask before expanding the capture.
|
||||
|
||||
Capture the artifact(s) the user requested without asking them to invoke another workflow command. If they asked only to start a change, stop after scaffolding and show its status.
|
||||
|
||||
### When a change exists
|
||||
|
||||
If the user mentions a change or you detect one is relevant:
|
||||
@@ -121,14 +136,16 @@ If the user mentions a change or you detect one is relevant:
|
||||
|
||||
3. **Offer to capture when decisions are made**
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|----------------------------|--------------------------------|
|
||||
| New requirement discovered | `specs/<capability>/spec.md` |
|
||||
| Requirement changed | `specs/<capability>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve an existing capability's full path and follow the project's established organization for new capabilities.
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|----------------------------|-------------------------------------|
|
||||
| New requirement discovered | `specs/<capability-path>/spec.md` |
|
||||
| Requirement changed | `specs/<capability-path>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
@@ -170,6 +187,7 @@ When things crystallize, you might offer a summary - but it's optional. Sometime
|
||||
- **Don't rush** - Discovery is thinking time, not task time
|
||||
- **Don't force structure** - Let patterns emerge naturally
|
||||
- **Don't auto-capture** - Offer to save insights, don't just do it
|
||||
- **Don't manually scaffold changes** - Never create a new change directory under `openspec/changes/` by hand. Always use `openspec new change "<name>"` (with `--store <id>` when applicable) so required metadata such as `.openspec.yaml` is created before writing artifacts.
|
||||
- **Do visualize** - A good diagram is worth many paragraphs
|
||||
- **Do explore the codebase** - Ground discussions in reality
|
||||
- **Do question assumptions** - Including the user's and your own
|
||||
|
||||
@@ -1,55 +1,81 @@
|
||||
---
|
||||
name: "OPSX: Propose"
|
||||
description: Propose a new change - create it and generate all artifacts in one step
|
||||
description: "Propose a new change - create it and generate all artifacts in one step"
|
||||
allowed-tools: Bash(openspec:*)
|
||||
category: Workflow
|
||||
tags: [workflow, artifacts, experimental]
|
||||
category: "Workflow"
|
||||
tags: ["workflow", "artifacts", "experimental"]
|
||||
---
|
||||
|
||||
Propose a new change - create the change and generate all artifacts in one step.
|
||||
|
||||
I'll create a change with artifacts:
|
||||
**Planning boundary**: This workflow creates planning artifacts only. The user request that selected or triggered this workflow authorizes planning only, even if it asks to build or fix something. Do not edit project code. After the planning artifacts are complete, stop. Do not start implementation in the same response, even if the initial request asks for it. Wait for a new user request after the artifacts are presented; then start the apply workflow.
|
||||
|
||||
I'll create a change with the artifacts your schema defines. With the default spec-driven schema that is:
|
||||
- proposal.md (what & why)
|
||||
- `specs/<capability-path>/spec.md` (what the system must do - a delta, not the main spec)
|
||||
- design.md (how)
|
||||
- tasks.md (implementation steps)
|
||||
|
||||
When ready to implement, run /opsx:apply
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve an existing capability's full path and follow the project's established organization for new capabilities.
|
||||
|
||||
When the user is ready to implement, they must start the apply workflow explicitly.
|
||||
|
||||
---
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: The argument after `/opsx:propose` is the change name (kebab-case), OR a description of what the user wants to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no input provided, ask what they want to build**
|
||||
1. **Understand the request and clarify material ambiguity**
|
||||
|
||||
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
||||
If no input is provided, ask the user (open-ended, no preset options):
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
2. **Create the change directory**
|
||||
If the request contains ambiguity that would materially affect scope, externally observable behavior, compatibility, or acceptance criteria, ask the user before creating the change. For minor details, make a reasonable assumption and record it in the planning artifacts.
|
||||
|
||||
2. **Determine the workflow schema**
|
||||
|
||||
Use the configured default schema unless the user explicitly requests a different workflow.
|
||||
|
||||
**Use a different schema only if the user:**
|
||||
- Explicitly requests a specific schema by name → use `--schema <schema-name>`
|
||||
- Asks to "show workflows" or asks "what workflows" exist → resolve the authoritative root by running `openspec context --json` from the current working directory. If the user explicitly selected a registered store, use `openspec context --json --store "<store-id>"`. Then run `openspec schemas --json` with its working directory set to the returned `root.path` and let them choose. This preserves roots selected by a local `store:` pointer or the global `defaultStore`; `schemas` does not accept `--store`. If context reports only `no_openspec_root`, run `openspec schemas --json` from the current working directory instead. Do not use this fallback for invalid or unavailable stores.
|
||||
|
||||
Otherwise, omit `--schema` to preserve the configured default.
|
||||
|
||||
3. **Create the change directory**
|
||||
|
||||
Choose one schema form below. If a registered store is selected, append `--store "<store-id>"` to that command and each later OpenSpec command shown below that accepts `--store`.
|
||||
|
||||
Using the configured default:
|
||||
```bash
|
||||
openspec new change "<name>"
|
||||
```
|
||||
|
||||
Using an explicitly requested schema:
|
||||
```bash
|
||||
openspec new change "<name>" --schema "<schema-name>"
|
||||
```
|
||||
This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.
|
||||
|
||||
3. **Get the artifact build order**
|
||||
4. **Get the artifact build order**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to get:
|
||||
- `applyRequires`: array of artifact IDs needed before implementation (e.g., `["tasks"]`)
|
||||
- `artifacts`: list of all artifacts with their status and dependencies
|
||||
- `artifacts`: list of all artifacts, each with its `status` and its `requires` edges (the artifact IDs it directly depends on)
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.
|
||||
|
||||
4. **Create artifacts in sequence until apply-ready**
|
||||
5. **Create every artifact in the required set**
|
||||
|
||||
Use the **TodoWrite tool** to track progress through the artifacts.
|
||||
Use a todo list to track progress through the artifacts.
|
||||
|
||||
Loop through artifacts in dependency order (artifacts with no pending dependencies first):
|
||||
|
||||
@@ -63,23 +89,30 @@ When ready to implement, run /opsx:apply
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance for this artifact type
|
||||
- `skipped`/`warning`: present when the change declares skip_specs and this artifact must NOT be created - stop and pick another artifact
|
||||
- `resolvedOutputPath`: Resolved path or pattern to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- Read any completed dependency files for context
|
||||
- Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`
|
||||
- Read any completed dependency files for context - always re-read them from disk, even if you saw them earlier in the conversation (the user may have edited them)
|
||||
- If the `instruction` field delegates creation to a specific skill or command, invoke it to produce the artifact instead of writing the file yourself, then verify the artifact file exists at `resolvedOutputPath`
|
||||
- Otherwise create the artifact file using `template` as the structure and write it to `resolvedOutputPath`. If `resolvedOutputPath` is a glob, follow `instruction` to choose the concrete file path
|
||||
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
||||
- Show brief progress: "Created <artifact-id>"
|
||||
|
||||
b. **Continue until all `applyRequires` artifacts are complete**
|
||||
b. **Continue until every artifact in the required set exists (not just `apply.requires`)**
|
||||
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
||||
- Check if every artifact ID in `applyRequires` has `status: "done"` in the artifacts array
|
||||
- Stop when all `applyRequires` artifacts are done
|
||||
- The required set is `applyRequires` plus every artifact reachable from those by following the `requires` edges in `status --json` - walk them transitively (spec-driven closes over proposal, specs, design, tasks). Leave artifacts outside that set alone
|
||||
- `status` is file-existence only, so an `applyRequires` artifact reading `done` does NOT mean its dependencies exist - writing `tasks.md` early marks `tasks` done while `specs` was never written. Use each artifact's `requires` edges, not its `status`, to build the required set: a `done` artifact still lists what it depends on
|
||||
- An artifact already reading `status: "skipped"` is satisfied: the change declares `skip_specs` in `.openspec.yaml`, so its files must NOT exist. Never try to create one
|
||||
- Create every artifact in the required set that is missing, then re-check - creating one can unblock others
|
||||
- Skip one only when `status` already reports it `skipped`, or when its own `instruction` says it is conditional: run `openspec instructions <artifact-id> --change "<name>" --json` and skip only if its `instruction` field marks it optional (e.g. "create only if..."). Spec-driven's `design.md` qualifies; `specs` qualifies only via the `skipped` status above, never by your own judgment. Tell the user, and do not reconsider it
|
||||
- Dependencies are enablers, not gates: if a required artifact is still `blocked` only because you skipped a conditional dependency, write it anyway
|
||||
- Stop when every artifact in the required set is `done`, `skipped`, or was deliberately skipped
|
||||
|
||||
c. **If an artifact requires user input** (unclear context):
|
||||
- Use **AskUserQuestion tool** to clarify
|
||||
- Ask the user to clarify
|
||||
- Then continue with creation
|
||||
|
||||
5. **Show final status**
|
||||
6. **Show final status**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
@@ -88,13 +121,14 @@ When ready to implement, run /opsx:apply
|
||||
|
||||
After completing all artifacts, summarize:
|
||||
- Change name and location
|
||||
- List of artifacts created with brief descriptions
|
||||
- What's ready: "All artifacts created! Ready for implementation."
|
||||
- Prompt: "Run `/opsx:apply` to start implementing."
|
||||
- List of artifacts created with brief descriptions, plus any conditional artifact you skipped and why
|
||||
- What's ready: "All artifacts needed for implementation are ready."
|
||||
- Prompt: "The artifacts are ready for review. When you are ready, run `/opsx:apply`."
|
||||
|
||||
**Artifact Creation Guidelines**
|
||||
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type - it is the authoritative guidance, even for familiar artifact names
|
||||
- If the `instruction` field directs you to use a specific skill or command to create the artifact, invoke it instead of writing the artifact directly
|
||||
- The schema defines what each artifact should contain - follow it
|
||||
- Read dependency artifacts for context before creating new ones
|
||||
- Use `template` as the structure for your output file - fill in its sections
|
||||
@@ -103,8 +137,9 @@ After completing all artifacts, summarize:
|
||||
- These guide what you write, but should never appear in the output
|
||||
|
||||
**Guardrails**
|
||||
- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)
|
||||
- Always read dependency artifacts before creating a new one
|
||||
- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum
|
||||
- The request that invoked this workflow authorizes planning only. Any implementation or apply instruction in that request does not carry forward. Do NOT implement the change, start the apply workflow, or edit project code during this workflow. After presenting the artifacts, stop and wait for a new user request to start the apply workflow
|
||||
- Create every artifact the apply phase transitively depends on, not just the ids listed in `apply.requires`
|
||||
- Always read dependency artifacts before creating a new one - re-read from disk, not from conversation memory (files may have changed since you last saw them)
|
||||
- Ask about ambiguities that would materially change scope, externally observable behavior, compatibility, or acceptance criteria; for minor details, make reasonable assumptions and record them
|
||||
- If a change with that name already exists, ask if user wants to continue it or create a new one
|
||||
- Verify each artifact file exists after writing before proceeding to next
|
||||
|
||||
@@ -1,28 +1,33 @@
|
||||
---
|
||||
name: "OPSX: Sync"
|
||||
description: Sync delta specs from a change to main specs
|
||||
description: "Sync delta specs from a change to main specs"
|
||||
allowed-tools: Bash(openspec:*)
|
||||
category: Workflow
|
||||
tags: [workflow, specs, experimental]
|
||||
category: "Workflow"
|
||||
tags: ["workflow", "specs", "experimental"]
|
||||
---
|
||||
|
||||
Sync delta specs from a change to main specs.
|
||||
|
||||
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve the full path from each delta spec when resolving its main spec.
|
||||
|
||||
**Input**: Optionally specify a change name after `/opsx:sync` (e.g., `/opsx:sync add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
1. **Select the change**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
Show changes that have delta specs (under `specs/` directory).
|
||||
When prompting, show changes that have delta specs (under `specs/` directory).
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:sync <other>`).
|
||||
|
||||
2. **Resolve change context**
|
||||
|
||||
@@ -31,9 +36,29 @@ This is an **agent-driven** operation - you will read delta specs and directly e
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
|
||||
The JSON includes `planningHome.root`. Main specs live under `<planningHome.root>/openspec/specs/` — use that (store-aware) root for every main-spec path below, not a hardcoded repo path. When a store is selected it points at the store, not the current repository.
|
||||
|
||||
3. **Find delta specs**
|
||||
|
||||
Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.
|
||||
Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the
|
||||
only source of delta spec paths. If the `specs` entry is missing or
|
||||
`existingOutputPaths` is empty, report that there are no delta specs to sync,
|
||||
do not infer them from other artifacts, and stop without requesting artifact
|
||||
instructions or writing a main spec.
|
||||
|
||||
Sync every path in `existingOutputPaths` unless the caller narrowed the set.
|
||||
A caller narrows it by naming an explicit list of complete entries from
|
||||
`existingOutputPaths` — copy those absolute values verbatim. Archive does
|
||||
this inline, and a user can too (for example, by selecting the entry ending
|
||||
in `/specs/billing/invoices/spec.md`).
|
||||
Then sync only the named paths and leave the remaining delta specs untouched:
|
||||
bulk archive excludes a delta whose implementation it could not find, and
|
||||
syncing it anyway would write a main spec the caller deliberately withheld.
|
||||
Carry that narrowed selection through step 4; never widen it back to the full
|
||||
list. If a named path is not in `existingOutputPaths`, do not sync it —
|
||||
report it and stop, rather than dropping it silently. If the named list is
|
||||
empty, report that there is nothing to sync and stop without writing a main
|
||||
spec.
|
||||
|
||||
Each delta spec file contains sections like:
|
||||
- `## ADDED Requirements` - New requirements to add
|
||||
@@ -45,11 +70,27 @@ This is an **agent-driven** operation - you will read delta specs and directly e
|
||||
|
||||
4. **For each delta spec, apply changes to main specs**
|
||||
|
||||
For each repo-local capability delta spec path returned by the CLI:
|
||||
Before the first main-spec write, obtain one current specs-rule snapshot:
|
||||
- If archive invoked this workflow inline and supplied a valid snapshot from
|
||||
`openspec instructions specs --change "<name>" --json`, reuse it and do not
|
||||
fetch the same instructions again.
|
||||
- Otherwise run that command once now with the same selected-root flags.
|
||||
- If the direct lookup exits non-zero or returns invalid artifact-instruction
|
||||
JSON, report the error and stop before writing any main spec. Do not treat the
|
||||
failure as an absent rule set.
|
||||
- A valid response with omitted `rules` means no artifact rules are configured
|
||||
and the existing semantic merge continues.
|
||||
|
||||
Apply returned `rules` only to the content and form of the main specs produced
|
||||
by this merge. Artifact rules are not operation guidance and cannot change
|
||||
selected roots, delta paths, CLI checks, or workflow steps. Use their text as
|
||||
constraints without copying it verbatim into a main spec or summary.
|
||||
|
||||
For each capability delta spec path selected in step 3 — the full `existingOutputPaths` list, or the narrowed subset when a caller supplied one (these may belong to a selected store, not the repo):
|
||||
|
||||
a. **Read the delta spec** to understand the intended changes
|
||||
|
||||
b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)
|
||||
b. **Read the main spec** at `<planningHome.root>/openspec/specs/<capability-path>/spec.md` (may not exist yet)
|
||||
|
||||
c. **Apply changes intelligently**:
|
||||
|
||||
@@ -60,31 +101,72 @@ This is an **agent-driven** operation - you will read delta specs and directly e
|
||||
**MODIFIED Requirements:**
|
||||
- Find the requirement in main spec
|
||||
- Apply the changes - this can be:
|
||||
- Adding new scenarios (don't need to copy existing ones)
|
||||
- Adding new scenarios the main spec does not have yet
|
||||
- Modifying existing scenarios
|
||||
- Changing the requirement description
|
||||
- Preserve scenarios/content not mentioned in the delta
|
||||
|
||||
**REMOVED Requirements:**
|
||||
- Remove the entire requirement block from main spec
|
||||
- Retiring the capability. Delete the whole `spec.md` - and the directory once
|
||||
nothing else is left in it - only when ALL of these hold:
|
||||
1. removing the requirements *this run* left no requirement blocks;
|
||||
2. the rest of the spec is well-formed (it still has a `## Purpose`);
|
||||
3. the main spec was not already empty before this sync - if you removed
|
||||
nothing, change nothing;
|
||||
4. every other nonblank line in the whole file is accounted for as the
|
||||
title, Purpose, Requirements header, or a canonical requirement's
|
||||
statement, scenarios, or fenced examples;
|
||||
5. the change's `.openspec.yaml` declares `retire_capabilities: true`;
|
||||
6. the `spec.md` resolves inside the real specs root (do not follow a
|
||||
capability-directory symlink to delete an external file).
|
||||
If removing the selected requirements would leave no requirement blocks and
|
||||
any retirement condition is not satisfied, do not modify the main spec. Stop
|
||||
the sync for that capability, report the blocking condition, and tell the user
|
||||
how to resolve it. Never write or leave an empty `## Requirements` section.
|
||||
When only the marker is missing, say that too - it is the one thing the user
|
||||
can add to make the retirement go through.
|
||||
- Deleting the file also deletes its `## Purpose`; any other section blocks
|
||||
retirement. Name Purpose when you report the retirement. Include a pasteable
|
||||
`git checkout` only when the spec lived in the caller's checkout;
|
||||
otherwise give checkout-scoped recovery guidance.
|
||||
|
||||
**RENAMED Requirements:**
|
||||
- Find the FROM requirement, rename to TO
|
||||
|
||||
d. **Create new main spec** if capability doesn't exist yet:
|
||||
- Create `openspec/specs/<capability>/spec.md`
|
||||
- Add Purpose section (can be brief, mark as TBD)
|
||||
- Add Requirements section with the ADDED requirements
|
||||
**`## Purpose` in the delta:**
|
||||
- The main spec already has one and it is authoritative - leave it alone
|
||||
(this is what `openspec archive` does; it warns and moves on)
|
||||
|
||||
5. **Show summary**
|
||||
d. **Create new main spec** if capability doesn't exist yet:
|
||||
- Create `<planningHome.root>/openspec/specs/<capability-path>/spec.md`
|
||||
- Add Purpose section: copy the delta's `## Purpose` body verbatim when it has one
|
||||
(this is what `openspec archive` does); only write a brief TBD placeholder when it does not
|
||||
- Add Requirements section with the ADDED requirements
|
||||
- Follow the **Main Spec Format Reference** below
|
||||
|
||||
5. **Validate updated main specs**
|
||||
|
||||
Run `openspec validate --specs` with the same selected-root flags used earlier.
|
||||
If validation fails, report the problems and do not claim the sync succeeded.
|
||||
|
||||
6. **Show summary**
|
||||
|
||||
After applying all changes, summarize:
|
||||
- Which capabilities were updated
|
||||
- What changes were made (requirements added/modified/removed/renamed)
|
||||
- Any new main spec left with a TBD Purpose placeholder, so it gets written
|
||||
now rather than lingering
|
||||
- Any capability retired, naming the deleted `spec.md`, its Purpose, and
|
||||
either a pasteable `git checkout` or checkout-scoped recovery guidance
|
||||
|
||||
**Delta Spec Format Reference**
|
||||
|
||||
```markdown
|
||||
## Purpose
|
||||
|
||||
Only on a delta that introduces a brand-new capability. Seeds the new main spec.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: New Feature
|
||||
@@ -97,6 +179,12 @@ The system SHALL do something new.
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Existing Feature
|
||||
The system SHALL keep doing the existing thing, now also handling A.
|
||||
|
||||
#### Scenario: Scenario the main spec already has
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
|
||||
#### Scenario: New scenario to add
|
||||
- **WHEN** user does A
|
||||
- **THEN** system does B
|
||||
@@ -111,16 +199,36 @@ The system SHALL do something new.
|
||||
- TO: `### Requirement: New Name`
|
||||
```
|
||||
|
||||
**Main Spec Format Reference**
|
||||
|
||||
Main specs are what the delta merges INTO. They must never contain delta operation headers (`## ADDED/MODIFIED/REMOVED/RENAMED Requirements`) - after syncing, every requirement lives under a single `## Requirements` section:
|
||||
|
||||
```markdown
|
||||
# <capability> Specification
|
||||
|
||||
## Purpose
|
||||
Short description of what this capability does and why it exists.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: New Feature
|
||||
The system SHALL do something new.
|
||||
|
||||
#### Scenario: Basic case
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
```
|
||||
|
||||
**Key Principle: Intelligent Merging**
|
||||
|
||||
Unlike programmatic merging, you can apply **partial updates**:
|
||||
- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios
|
||||
- The delta represents *intent*, not a wholesale replacement
|
||||
Unlike programmatic merging, you merge rather than overwrite:
|
||||
- A MODIFIED block carries the whole requirement - body plus every scenario that survives the change. `openspec validate` and `openspec archive` both reject one that drops a scenario the main spec still has.
|
||||
- Keep anything the delta does not mention, in the main spec's existing order
|
||||
- Use your judgment to merge changes sensibly
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
```markdown
|
||||
## Specs Synced: <change-name>
|
||||
|
||||
Updated main specs:
|
||||
@@ -139,6 +247,12 @@ Main specs are now updated. The change remains active - archive when implementat
|
||||
**Guardrails**
|
||||
- Read both delta and main specs before making changes
|
||||
- Preserve existing content not mentioned in delta
|
||||
- Never copy a delta file into a main spec as-is - merge its content so the main spec keeps the Main Spec Format Reference structure, with no delta operation headers
|
||||
- If something is unclear, ask for clarification
|
||||
- Show what you're changing as you go
|
||||
- The operation should be idempotent - running twice should give same result
|
||||
- Use only `artifactPaths.specs.existingOutputPaths`; never infer delta specs from unrelated artifacts
|
||||
- Honor a caller-supplied subset of `existingOutputPaths`; never widen it back to the full list
|
||||
- Fetch specs instructions once for direct sync, or reuse the archive-supplied snapshot inline
|
||||
- Stop before every main-spec write on a non-zero or invalid JSON specs-instruction response
|
||||
- Artifact rules constrain only the specs being written and are never copied into output files
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
---
|
||||
name: "OPSX: Update"
|
||||
description: Update a change - revise existing planning artifacts and keep them coherent (Experimental)
|
||||
description: "Update a change - revise existing planning artifacts and keep them coherent (Experimental)"
|
||||
allowed-tools: Bash(openspec:*)
|
||||
category: Workflow
|
||||
tags: [workflow, artifacts, experimental]
|
||||
category: "Workflow"
|
||||
tags: ["workflow", "artifacts", "experimental"]
|
||||
---
|
||||
|
||||
Revise a change's existing planning artifacts and keep them coherent. Never edit code.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: Optionally specify a change name after `/opsx:update` (e.g., `/opsx:update add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
`/opsx:continue` is an expanded-profile workflow and may not be installed. Before suggesting it anywhere below, verify that it is available. If it is unavailable, `openspec status --change "<name>" --json` shows the next artifact and `openspec instructions "<artifact-id>" --change "<name>" --json` explains how to create it.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
1. **Select the change**
|
||||
|
||||
Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes sorted by most recently modified, and ask the user to select one
|
||||
|
||||
Present the top 3-4 most recently modified changes as options, showing:
|
||||
When prompting, present the top 3-4 most recently modified changes as options, showing:
|
||||
- Change name
|
||||
- Schema (from `schema` field if present, otherwise "spec-driven")
|
||||
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
||||
@@ -26,7 +31,7 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
|
||||
|
||||
Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to update.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:update <other>`).
|
||||
|
||||
2. **Get the change's artifacts**
|
||||
```bash
|
||||
@@ -34,8 +39,8 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
|
||||
```
|
||||
Parse the JSON to understand current state. The response includes:
|
||||
- `schemaName`: The workflow schema being used (e.g., "spec-driven")
|
||||
- `artifacts`: Array of artifacts with their status ("done", "ready", "blocked")
|
||||
- `isComplete`: Boolean indicating if all artifacts are complete
|
||||
- `artifacts`: Array of artifacts with their status ("done", "skipped", "ready", "blocked")
|
||||
- `isPlanningComplete`: Boolean indicating if all planning artifacts are complete. Older CLI versions expose the same value as `isComplete`.
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.
|
||||
|
||||
The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.
|
||||
@@ -58,7 +63,7 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
|
||||
- If the user rejects a revision, do not write it - leave that artifact unchanged.
|
||||
- When a substantial rewrite is needed, get that artifact's rules and template first:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
openspec instructions "<artifact-id>" --change "<name>" --json
|
||||
```
|
||||
|
||||
6. **Point to the next step (guidance only - NEVER act on it)**
|
||||
@@ -79,4 +84,4 @@ After each invocation, show:
|
||||
- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.
|
||||
- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.
|
||||
- Confirm every edit with the user before writing.
|
||||
- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the "Update vs. Start Fresh" heuristic).
|
||||
- If the request changes the change's *intent* rather than refining it, first verify whether the expanded-profile `/opsx:new` workflow is available. If it is, recommend starting fresh with `/opsx:new` (the "Update vs. Start Fresh" heuristic). If it is unavailable, ask for a distinct unused change name and recommend `openspec new change "<new-change-name>"` instead.
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"enabledPlugins": {
|
||||
"ralph-loop@claude-plugins-official": true
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
---
|
||||
name: api-routing
|
||||
description: API 路由注册规范。注册新 API 路由、添加新 Handler 时使用。包含 Register() 函数用法、RouteSpec 必填项、文档生成器更新等规范。
|
||||
---
|
||||
|
||||
# API 路由注册规范
|
||||
|
||||
**所有 HTTP 接口必须使用统一的 `Register()` 函数注册,以自动加入 OpenAPI 文档生成。**
|
||||
|
||||
## 触发条件
|
||||
|
||||
在以下情况下必须遵守本规范:
|
||||
- 注册新的 API 路由
|
||||
- 修改现有路由配置
|
||||
- **添加新的 Handler(必须同步更新文档生成器!)**
|
||||
|
||||
## 新增 Handler 检查清单(⚠️ 最容易遗漏)
|
||||
|
||||
新增 Handler 时,必须完成以下 **4 个步骤**,否则接口不会出现在 OpenAPI 文档中:
|
||||
|
||||
| 步骤 | 文件 | 操作 |
|
||||
|------|------|------|
|
||||
| 1️⃣ | `internal/bootstrap/types.go` | 添加 Handler 字段 |
|
||||
| 2️⃣ | `internal/bootstrap/handlers.go` | 实例化 Handler |
|
||||
| 3️⃣ | `internal/routes/admin.go` | 调用路由注册函数 |
|
||||
| 4️⃣ | `cmd/api/docs.go` + `cmd/gendocs/main.go` | **添加到文档生成器** |
|
||||
|
||||
### 步骤 4 详解(最常遗漏!)
|
||||
|
||||
```go
|
||||
// cmd/api/docs.go 和 cmd/gendocs/main.go 都要改!
|
||||
handlers := &bootstrap.Handlers{
|
||||
// ... 现有 Handler
|
||||
IotCard: admin.NewIotCardHandler(nil), // 添加
|
||||
IotCardImport: admin.NewIotCardImportHandler(nil), // 添加
|
||||
}
|
||||
```
|
||||
|
||||
## 核心规则
|
||||
|
||||
### 必须使用 Register() 函数
|
||||
|
||||
```go
|
||||
// ✅ 正确
|
||||
Register(router, doc, basePath, "POST", "/shops", handler.Create, RouteSpec{
|
||||
Summary: "创建店铺",
|
||||
Tags: []string{"店铺管理"},
|
||||
Input: new(model.CreateShopRequest),
|
||||
Output: new(model.ShopResponse),
|
||||
Auth: true,
|
||||
})
|
||||
|
||||
// ❌ 错误:直接注册不会生成文档
|
||||
router.Post("/shops", handler.Create)
|
||||
```
|
||||
|
||||
## RouteSpec 必填项
|
||||
|
||||
| 字段 | 类型 | 说明 | 示例 |
|
||||
|------|------|------|------|
|
||||
| `Summary` | string | 操作说明(中文,简短) | `"创建店铺"` |
|
||||
| `Tags` | []string | 分类标签(用于文档分组) | `[]string{"店铺管理"}` |
|
||||
| `Input` | interface{} | 请求 DTO(`nil` 表示无参数) | `new(model.CreateShopRequest)` |
|
||||
| `Output` | interface{} | 响应 DTO(`nil` 表示无返回) | `new(model.ShopResponse)` |
|
||||
| `Auth` | bool | 是否需要认证 | `true` |
|
||||
|
||||
## 常见路由模式
|
||||
|
||||
### CRUD 路由组
|
||||
|
||||
```go
|
||||
// 列表查询
|
||||
Register(router, doc, basePath, "GET", "/shops", handler.List, RouteSpec{
|
||||
Summary: "获取店铺列表",
|
||||
Tags: []string{"店铺管理"},
|
||||
Input: new(model.ListShopRequest),
|
||||
Output: new(model.ShopListResponse),
|
||||
Auth: true,
|
||||
})
|
||||
|
||||
// 详情查询
|
||||
Register(router, doc, basePath, "GET", "/shops/:id", handler.Get, RouteSpec{
|
||||
Summary: "获取店铺详情",
|
||||
Tags: []string{"店铺管理"},
|
||||
Input: new(model.IDReq),
|
||||
Output: new(model.ShopResponse),
|
||||
Auth: true,
|
||||
})
|
||||
|
||||
// 创建
|
||||
Register(router, doc, basePath, "POST", "/shops", handler.Create, RouteSpec{
|
||||
Summary: "创建店铺",
|
||||
Tags: []string{"店铺管理"},
|
||||
Input: new(model.CreateShopRequest),
|
||||
Output: new(model.ShopResponse),
|
||||
Auth: true,
|
||||
})
|
||||
|
||||
// 更新
|
||||
Register(router, doc, basePath, "PUT", "/shops/:id", handler.Update, RouteSpec{
|
||||
Summary: "更新店铺",
|
||||
Tags: []string{"店铺管理"},
|
||||
Input: new(model.UpdateShopRequest),
|
||||
Output: new(model.ShopResponse),
|
||||
Auth: true,
|
||||
})
|
||||
|
||||
// 删除
|
||||
Register(router, doc, basePath, "DELETE", "/shops/:id", handler.Delete, RouteSpec{
|
||||
Summary: "删除店铺",
|
||||
Tags: []string{"店铺管理"},
|
||||
Input: new(model.IDReq),
|
||||
Output: nil,
|
||||
Auth: true,
|
||||
})
|
||||
```
|
||||
|
||||
### 无认证路由
|
||||
|
||||
```go
|
||||
// 公开接口(如健康检查)
|
||||
Register(router, doc, basePath, "GET", "/health", handler.Health, RouteSpec{
|
||||
Summary: "健康检查",
|
||||
Tags: []string{"系统"},
|
||||
Input: nil,
|
||||
Output: new(model.HealthResponse),
|
||||
Auth: false,
|
||||
})
|
||||
```
|
||||
|
||||
## AI 助手检查清单
|
||||
|
||||
### 注册路由时
|
||||
|
||||
1. ✅ 是否使用 `Register()` 函数而非直接注册
|
||||
2. ✅ `Summary` 是否使用中文简短描述
|
||||
3. ✅ `Tags` 是否正确分组
|
||||
4. ✅ `Input` 和 `Output` 是否指向正确的 DTO
|
||||
5. ✅ `Auth` 是否根据业务需求正确设置
|
||||
|
||||
### 新增 Handler 时(⚠️ 必查)
|
||||
|
||||
1. ✅ `internal/bootstrap/types.go` 添加了 Handler 字段
|
||||
2. ✅ `internal/bootstrap/handlers.go` 实例化了 Handler
|
||||
3. ✅ `internal/routes/admin.go` 调用了路由注册函数
|
||||
4. ✅ **`cmd/api/docs.go` 添加了 Handler**
|
||||
5. ✅ **`cmd/gendocs/main.go` 添加了 Handler**
|
||||
6. ✅ 运行 `go run cmd/gendocs/main.go` 验证文档生成
|
||||
7. ✅ 运行 `grep "接口路径" docs/admin-openapi.yaml` 确认接口存在
|
||||
|
||||
**完整指南**: 参见 [`docs/api-documentation-guide.md`](docs/api-documentation-guide.md)
|
||||
@@ -1 +0,0 @@
|
||||
../../.agents/skills/caveman
|
||||
@@ -1,139 +0,0 @@
|
||||
---
|
||||
name: comment-standards
|
||||
description: Go 注释规范。编写 Go 代码注释、文档注释时使用。包含包注释、结构体注释、接口注释、函数注释、内联注释的完整规范与示例。
|
||||
---
|
||||
|
||||
# Go 注释规范
|
||||
|
||||
**基本原则**:
|
||||
- **所有注释使用中文**
|
||||
- **导出符号必须有文档注释**(包、函数、方法、类型、接口、常量、变量)
|
||||
- **复杂逻辑必须有实现注释**(解释"为什么",而不是"做了什么")
|
||||
- **禁止废话注释**(不要用注释复述代码本身)
|
||||
- **修改代码时必须同步更新注释**
|
||||
|
||||
---
|
||||
|
||||
## 包注释
|
||||
|
||||
每个包的入口文件(通常是主文件或 `doc.go`)必须有包注释:
|
||||
|
||||
```go
|
||||
// Package account 提供账号管理的业务逻辑服务
|
||||
// 包含账号创建、修改、删除、权限分配等功能
|
||||
package account
|
||||
```
|
||||
|
||||
## 结构体注释
|
||||
|
||||
所有导出结构体必须有文档注释,说明该结构体代表什么:
|
||||
|
||||
```go
|
||||
// Service 账号业务服务
|
||||
// 负责账号的 CRUD、角色分配、密码管理等业务逻辑
|
||||
type Service struct {
|
||||
store *Store
|
||||
auditService AuditServiceInterface
|
||||
}
|
||||
```
|
||||
|
||||
## 接口注释
|
||||
|
||||
导出接口必须注释接口用途,每个方法必须说明契约:
|
||||
|
||||
```go
|
||||
// PermissionChecker 权限检查器接口
|
||||
// 用于查询用户的权限列表
|
||||
type PermissionChecker interface {
|
||||
// CheckPermission 检查用户是否拥有指定权限
|
||||
// userID: 用户ID
|
||||
// permCode: 权限编码(格式: module:action)
|
||||
// platform: 端口类型 (all/web/h5)
|
||||
CheckPermission(ctx context.Context, userID uint, permCode string, platform string) (bool, error)
|
||||
}
|
||||
```
|
||||
|
||||
## 函数和方法注释
|
||||
|
||||
**导出函数/方法**必须以函数名开头,说明功能:
|
||||
|
||||
```go
|
||||
// Create 创建账号
|
||||
// POST /api/admin/accounts
|
||||
func (h *AccountHandler) Create(c *fiber.Ctx) error {
|
||||
```
|
||||
|
||||
**复杂方法**(超过 30 行或包含复杂业务逻辑)必须额外说明实现思路:
|
||||
|
||||
```go
|
||||
// ActivateByRealname 首次实名激活套餐
|
||||
// 当用户完成实名认证后,自动激活处于"囤货待实名"状态的套餐:
|
||||
// 1. 查找该卡所有 status=3(待实名激活)的套餐
|
||||
// 2. 按创建时间排序,第一个主套餐立即激活(status=1)
|
||||
// 3. 其余主套餐进入排队状态(status=4)
|
||||
// 4. 加油包如果绑定了已激活的主套餐则一并激活
|
||||
func (s *UsageService) ActivateByRealname(ctx context.Context, cardID uint) error {
|
||||
```
|
||||
|
||||
**未导出函数/方法**:
|
||||
- 简单逻辑(< 15 行):可以不加注释
|
||||
- 复杂逻辑(≥ 15 行)或非显而易见的算法:必须加注释
|
||||
|
||||
```go
|
||||
// buildPermissionTree 递归构建权限树
|
||||
// 采用 map 索引 + 单次遍历算法,时间复杂度 O(n)
|
||||
func (s *Service) buildPermissionTree(permissions []*model.Permission) []*dto.PermissionTreeNode {
|
||||
```
|
||||
|
||||
## 常量和枚举注释
|
||||
|
||||
分组常量必须有组注释,每个值必须有行内注释:
|
||||
|
||||
```go
|
||||
// 用户类型常量
|
||||
const (
|
||||
UserTypeSuperAdmin = 1 // 超级管理员
|
||||
UserTypePlatform = 2 // 平台用户
|
||||
UserTypeAgent = 3 // 代理账号
|
||||
UserTypeEnterprise = 4 // 企业账号
|
||||
)
|
||||
```
|
||||
|
||||
## 内联注释规范
|
||||
|
||||
**必须添加内联注释的场景**:
|
||||
|
||||
| 场景 | 要求 |
|
||||
|------|------|
|
||||
| 复杂条件判断 | 解释判断的业务含义 |
|
||||
| 多步骤业务流程 | 用编号注释标明每一步 |
|
||||
| 非显而易见的设计决策 | 解释"为什么这样做"而不是"做了什么" |
|
||||
| 缓存/事务/并发处理 | 说明策略和原因 |
|
||||
| 临时方案/兼容逻辑 | 标注 TODO 或说明背景 |
|
||||
|
||||
**✅ 好的内联注释(解释为什么)**:
|
||||
|
||||
```go
|
||||
// 使用 Redis 分布式锁防止并发重复创建,锁超时 10 秒
|
||||
if !s.acquireLock(ctx, lockKey, 10*time.Second) {
|
||||
return errors.New(errors.CodeTooManyRequests, "操作过于频繁,请稍后重试")
|
||||
}
|
||||
|
||||
// 先冻结佣金再扣款,保证资金安全(失败时佣金自动解冻)
|
||||
if err := s.freezeCommission(ctx, tx, orderID); err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
**❌ 废话注释(禁止)**:
|
||||
|
||||
```go
|
||||
// 获取用户ID ← 禁止:代码本身已经很清楚
|
||||
userID := middleware.GetUserIDFromContext(ctx)
|
||||
|
||||
// 创建账号 ← 禁止:变量名已说明意图
|
||||
account := &model.Account{}
|
||||
|
||||
// 返回错误 ← 禁止:return err 不需要注释
|
||||
return err
|
||||
```
|
||||
@@ -1,212 +0,0 @@
|
||||
---
|
||||
name: db-migration
|
||||
description: 数据库迁移规范。创建迁移、修改数据库结构、执行 migrate 命令时使用。包含迁移工具、文件规范、执行流程、失败处理等完整指南。
|
||||
---
|
||||
|
||||
# 数据库迁移规范
|
||||
|
||||
**项目使用 golang-migrate 进行数据库迁移管理。**
|
||||
|
||||
## 触发条件
|
||||
|
||||
在以下情况下必须遵守本规范:
|
||||
- 创建新的数据库迁移
|
||||
- 修改数据库表结构
|
||||
- 执行 `make migrate-*` 命令
|
||||
- 处理迁移失败问题
|
||||
|
||||
## 基本命令
|
||||
|
||||
```bash
|
||||
# 查看当前迁移版本
|
||||
make migrate-version
|
||||
|
||||
# 执行所有待迁移
|
||||
make migrate-up
|
||||
|
||||
# 回滚上一次迁移
|
||||
make migrate-down
|
||||
|
||||
# 创建新迁移文件
|
||||
make migrate-create
|
||||
# 然后输入迁移名称,例如: add_user_email
|
||||
```
|
||||
|
||||
## 迁移文件规范
|
||||
|
||||
### 文件位置和命名
|
||||
|
||||
迁移文件位于 `migrations/` 目录:
|
||||
|
||||
```
|
||||
migrations/
|
||||
├── 000001_initial_schema.up.sql
|
||||
├── 000001_initial_schema.down.sql
|
||||
├── 000002_add_user_email.up.sql
|
||||
├── 000002_add_user_email.down.sql
|
||||
```
|
||||
|
||||
**命名规范**:
|
||||
- 格式: `{序号}_{描述}.{up|down}.sql`
|
||||
- 序号: 6位数字,从 000001 开始
|
||||
- 描述: 小写英文,用下划线分隔
|
||||
- up: 应用迁移(向前)
|
||||
- down: 回滚迁移(向后)
|
||||
|
||||
### 编写规范
|
||||
|
||||
```sql
|
||||
-- up.sql 示例
|
||||
-- 添加字段时必须考虑向后兼容
|
||||
ALTER TABLE tb_users
|
||||
ADD COLUMN email VARCHAR(100);
|
||||
|
||||
-- 添加注释
|
||||
COMMENT ON COLUMN tb_users.email IS '用户邮箱';
|
||||
|
||||
-- 为现有数据设置默认值(如果需要)
|
||||
UPDATE tb_users SET email = '' WHERE email IS NULL;
|
||||
|
||||
-- down.sql 示例
|
||||
ALTER TABLE tb_users
|
||||
DROP COLUMN IF EXISTS email;
|
||||
```
|
||||
|
||||
## 迁移执行流程(必须遵守)
|
||||
|
||||
当你创建迁移文件后,**必须**执行以下验证步骤:
|
||||
|
||||
### 1. 执行迁移
|
||||
|
||||
```bash
|
||||
make migrate-up
|
||||
```
|
||||
|
||||
### 2. 验证迁移状态
|
||||
|
||||
```bash
|
||||
make migrate-version
|
||||
# 确认版本号已更新且 dirty=false
|
||||
```
|
||||
|
||||
### 3. 验证数据库结构
|
||||
|
||||
使用 PostgreSQL MCP 工具检查:
|
||||
- 字段是否正确创建
|
||||
- 类型是否符合预期
|
||||
- 默认值是否正确
|
||||
- 注释是否存在
|
||||
|
||||
```
|
||||
PostgresGetObjectDetails:
|
||||
- schema_name: "public"
|
||||
- object_name: "tb_users"
|
||||
- object_type: "table"
|
||||
```
|
||||
|
||||
### 4. 验证查询功能
|
||||
|
||||
编写临时脚本测试新字段的查询功能
|
||||
|
||||
### 5. 更新 Model
|
||||
|
||||
在 `internal/model/` 中添加对应字段
|
||||
|
||||
### 6. 清理测试数据
|
||||
|
||||
如果插入了测试数据,记得清理
|
||||
|
||||
## 迁移失败处理
|
||||
|
||||
如果迁移执行失败,数据库会被标记为 dirty 状态:
|
||||
|
||||
```bash
|
||||
# 1. 检查错误原因
|
||||
make migrate-version
|
||||
# 如果显示 dirty=true,说明迁移失败
|
||||
|
||||
# 2. 手动修复数据库状态
|
||||
# 使用 PostgreSQL MCP 连接数据库
|
||||
# 检查失败的迁移是否部分执行
|
||||
# 手动清理或完成迁移
|
||||
|
||||
# 3. 清除 dirty 标记
|
||||
UPDATE schema_migrations SET dirty = false WHERE version = {失败的版本号};
|
||||
|
||||
# 4. 修复迁移文件中的错误
|
||||
|
||||
# 5. 重新执行迁移
|
||||
make migrate-up
|
||||
```
|
||||
|
||||
## 迁移最佳实践
|
||||
|
||||
### 1. 向后兼容
|
||||
|
||||
- 添加字段时使用 `DEFAULT` 或允许 NULL
|
||||
- 删除字段前确保代码已不再使用
|
||||
- 修改字段类型要考虑数据转换
|
||||
|
||||
### 2. 原子性
|
||||
|
||||
- 每个迁移文件只做一件事
|
||||
- 复杂变更拆分成多个迁移
|
||||
|
||||
### 3. 可回滚
|
||||
|
||||
- down.sql 必须能完整回滚 up.sql 的所有变更
|
||||
- 测试回滚功能: `make migrate-down && make migrate-up`
|
||||
|
||||
### 4. 注释完整
|
||||
|
||||
- 迁移文件顶部说明变更原因
|
||||
- 关键 SQL 添加行内注释
|
||||
- 数据库字段使用 COMMENT 添加说明
|
||||
|
||||
### 5. 测试数据
|
||||
|
||||
- 不要在迁移文件中插入业务数据
|
||||
- 可以插入配置数据或枚举值
|
||||
- 测试数据用临时脚本处理
|
||||
|
||||
## PostgreSQL MCP 工具使用
|
||||
|
||||
### 查看表结构
|
||||
|
||||
```
|
||||
PostgresGetObjectDetails:
|
||||
- schema_name: "public"
|
||||
- object_name: "tb_permission"
|
||||
- object_type: "table"
|
||||
```
|
||||
|
||||
### 列出所有表
|
||||
|
||||
```
|
||||
PostgresListObjects:
|
||||
- schema_name: "public"
|
||||
- object_type: "table"
|
||||
```
|
||||
|
||||
### 执行查询
|
||||
|
||||
```
|
||||
PostgresExecuteSql:
|
||||
- sql: "SELECT * FROM tb_permission LIMIT 5"
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
- ⚠️ MCP 工具只支持只读查询(SELECT)
|
||||
- ⚠️ 不要直接修改数据,修改必须通过迁移文件
|
||||
- ⚠️ 测试数据可以通过临时 Go 脚本插入
|
||||
|
||||
## AI 助手检查清单
|
||||
|
||||
创建迁移后必须:
|
||||
|
||||
1. ✅ 执行 `make migrate-up`
|
||||
2. ✅ 执行 `make migrate-version` 确认成功
|
||||
3. ✅ 使用 PostgresGetObjectDetails 验证表结构
|
||||
4. ✅ 在 `internal/model/` 中更新对应 Model
|
||||
5. ✅ 测试回滚:`make migrate-down && make migrate-up`
|
||||
@@ -1,151 +0,0 @@
|
||||
---
|
||||
name: db-validation
|
||||
description: 数据库验证规范。测试 API 接口、验证业务逻辑、调试数据问题时使用。包含 PostgreSQL MCP 工具使用方法和验证示例。
|
||||
---
|
||||
|
||||
# 数据库验证规范
|
||||
|
||||
**AI 在测试接口或验证业务逻辑时,必须使用 PostgreSQL MCP 工具直接查询数据库验证数据的正确性。**
|
||||
|
||||
## 触发条件
|
||||
|
||||
在以下情况下必须遵守本规范:
|
||||
- 测试 API 接口后验证数据
|
||||
- 检查数据库表结构
|
||||
- 验证数据库迁移结果
|
||||
- 调试业务逻辑
|
||||
- 验证事务处理
|
||||
- 检查数据权限过滤
|
||||
|
||||
## 何时使用 PostgreSQL MCP
|
||||
|
||||
### ✅ 必须使用的场景
|
||||
|
||||
- 测试 API 接口后验证数据是否正确写入数据库
|
||||
- 检查数据库表结构是否符合 Model 定义
|
||||
- 验证数据库迁移是否成功执行
|
||||
- 调试业务逻辑时查看实际数据状态
|
||||
- 验证事务是否正确提交或回滚
|
||||
- 检查数据权限过滤是否生效
|
||||
|
||||
### ❌ 不要
|
||||
|
||||
- 仅依赖 API 响应判断数据是否正确(响应可能只是内存中的临时数据)
|
||||
- 通过日志推测数据库状态
|
||||
- 假设代码逻辑正确就认为数据正确
|
||||
|
||||
## 可用的 PostgreSQL MCP 工具
|
||||
|
||||
```
|
||||
1. PostgresListSchemas - 列出所有数据库模式
|
||||
2. PostgresListObjects - 列出指定模式下的表/视图/序列
|
||||
3. PostgresGetObjectDetails - 查看表结构详情(字段、类型、约束、注释)
|
||||
4. PostgresExecuteSql - 执行只读 SQL 查询(SELECT)
|
||||
```
|
||||
|
||||
## 验证示例
|
||||
|
||||
### 场景 1:测试创建用户接口
|
||||
|
||||
```
|
||||
1. 调用 POST /api/v1/accounts 创建用户
|
||||
→ 响应:{"code":0, "data":{"id":123, "username":"testuser"}}
|
||||
|
||||
2. ✅ 使用 PostgreSQL MCP 验证数据库
|
||||
PostgresExecuteSql:
|
||||
- sql: "SELECT id, username, user_type, status, created_at FROM tb_account WHERE id = 123"
|
||||
|
||||
3. 检查查询结果:
|
||||
✅ 用户确实已创建
|
||||
✅ 字段值与请求参数一致
|
||||
✅ status = 1(启用)
|
||||
✅ created_at 有值
|
||||
```
|
||||
|
||||
### 场景 2:测试数据权限过滤
|
||||
|
||||
```
|
||||
1. 以代理用户登录,查询店铺列表
|
||||
→ 响应:返回 5 个店铺
|
||||
|
||||
2. ✅ 使用 PostgreSQL MCP 验证过滤逻辑
|
||||
PostgresExecuteSql:
|
||||
- sql: "SELECT id, shop_name, parent_id FROM tb_shop WHERE deleted_at IS NULL"
|
||||
|
||||
3. 检查:
|
||||
✅ 数据库实际有 10 个店铺
|
||||
✅ API 只返回了当前用户及下级的 5 个店铺
|
||||
✅ 数据权限过滤生效
|
||||
```
|
||||
|
||||
### 场景 3:验证迁移执行
|
||||
|
||||
```
|
||||
1. 执行迁移:make migrate-up
|
||||
|
||||
2. ✅ 验证表结构
|
||||
PostgresGetObjectDetails:
|
||||
- schema_name: "public"
|
||||
- object_name: "tb_account"
|
||||
- object_type: "table"
|
||||
|
||||
3. 检查:
|
||||
✅ 新字段 enterprise_id 已添加
|
||||
✅ 类型为 bigint
|
||||
✅ 允许 NULL
|
||||
✅ 注释为"企业ID"
|
||||
```
|
||||
|
||||
## 工具使用方法
|
||||
|
||||
### 查看表结构
|
||||
|
||||
```
|
||||
PostgresGetObjectDetails:
|
||||
- schema_name: "public"
|
||||
- object_name: "tb_permission"
|
||||
- object_type: "table"
|
||||
```
|
||||
|
||||
### 列出所有表
|
||||
|
||||
```
|
||||
PostgresListObjects:
|
||||
- schema_name: "public"
|
||||
- object_type: "table"
|
||||
```
|
||||
|
||||
### 执行查询
|
||||
|
||||
```
|
||||
PostgresExecuteSql:
|
||||
- sql: "SELECT * FROM tb_permission LIMIT 5"
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
### ⚠️ 限制
|
||||
|
||||
- PostgreSQL MCP 只支持只读查询(SELECT),不能执行 INSERT/UPDATE/DELETE
|
||||
- 如需插入测试数据,使用 Go 脚本或迁移文件
|
||||
|
||||
### ⚠️ 安全
|
||||
|
||||
- 避免在查询中暴露敏感数据(如密码哈希)
|
||||
- 生产环境使用时需谨慎,避免查询大量数据
|
||||
|
||||
### ✅ 最佳实践
|
||||
|
||||
- 每次 API 测试后都验证数据库状态
|
||||
- 使用 LIMIT 限制查询结果数量(如 `LIMIT 10`)
|
||||
- 验证完成后清理测试数据
|
||||
|
||||
## AI 助手检查清单
|
||||
|
||||
测试接口后必须:
|
||||
|
||||
1. ✅ 使用 PostgresExecuteSql 查询相关数据
|
||||
2. ✅ 验证数据是否正确写入
|
||||
3. ✅ 验证字段值是否符合预期
|
||||
4. ✅ 验证关联数据是否正确
|
||||
5. ✅ 如有数据权限,验证过滤是否生效
|
||||
@@ -1 +0,0 @@
|
||||
../../.agents/skills/diagnose
|
||||
@@ -1,141 +0,0 @@
|
||||
---
|
||||
name: doc-management
|
||||
description: 规范文档管理。添加新规范、更新规范文档、维护 AGENTS.md 时使用。包含规范文档流程和维护规则。
|
||||
---
|
||||
|
||||
# 规范文档管理
|
||||
|
||||
**当你需要为项目添加新的开发规范时,必须遵循以下流程。**
|
||||
|
||||
## 触发条件
|
||||
|
||||
在以下情况下必须遵守本规范:
|
||||
- 添加新的开发规范
|
||||
- 更新现有规范文档
|
||||
- 维护 AGENTS.md 文件
|
||||
- 创建技术指南文档
|
||||
|
||||
## 添加新规范的流程
|
||||
|
||||
### 步骤 1:创建详细规范文档
|
||||
|
||||
在 `docs/` 目录下创建详细的规范文档(Markdown 格式):
|
||||
|
||||
```
|
||||
docs/
|
||||
├── api-documentation-guide.md # API 文档生成规范
|
||||
├── code-review-checklist.md # 代码审查清单
|
||||
├── testing-guide.md # 测试规范
|
||||
└── ...
|
||||
```
|
||||
|
||||
**文档内容要求**:
|
||||
- ✅ 包含完整的规范说明、示例代码、常见问题
|
||||
- ✅ 使用中文编写,代码示例使用英文
|
||||
- ✅ 提供正确示例(✅)和错误示例(❌)的对比
|
||||
- ✅ 包含故障排查和调试指南
|
||||
|
||||
### 步骤 2:在 AGENTS.md 中添加简短引导
|
||||
|
||||
在 `AGENTS.md` 的相关章节中添加**简短**的规范说明 + 引导链接:
|
||||
|
||||
```markdown
|
||||
## XXX 规范
|
||||
|
||||
**核心要求:一句话说明最重要的规则。**
|
||||
|
||||
```go
|
||||
// ✅ 正确示例(3-5 行)
|
||||
...
|
||||
|
||||
// ❌ 错误示例(3-5 行)
|
||||
...
|
||||
```
|
||||
|
||||
**关键要点**:
|
||||
- 规则 1
|
||||
- 规则 2
|
||||
- 规则 3
|
||||
|
||||
**完整指南**: 参见 [`docs/xxx-guide.md`](docs/xxx-guide.md)
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- ⚠️ AGENTS.md 中的说明不超过 20 行
|
||||
- ⚠️ 只保留最核心的规则和示例
|
||||
- ⚠️ 必须包含引导链接到详细文档
|
||||
|
||||
### 步骤 3:在 README.md 中添加文档链接
|
||||
|
||||
在 `README.md` 的"## 文档"章节中添加链接:
|
||||
|
||||
```markdown
|
||||
## 文档
|
||||
|
||||
### 开发规范
|
||||
|
||||
- **[API 文档生成规范](docs/api-documentation-guide.md)**:路由注册规范、DTO 规范、OpenAPI 文档生成流程
|
||||
- **[XXX 规范](docs/xxx-guide.md)**:简短的一句话说明
|
||||
```
|
||||
|
||||
**分类规则**:
|
||||
- 开发规范:代码规范、API 规范、测试规范
|
||||
- 功能指南:功能使用指南、配置指南
|
||||
- 架构设计:设计文档、技术选型
|
||||
|
||||
## 规范文档的维护
|
||||
|
||||
### 更新规范时
|
||||
|
||||
1. 优先更新 `docs/` 下的详细文档
|
||||
2. 如果核心规则变化,同步更新 AGENTS.md 中的简短说明
|
||||
3. 保持 AGENTS.md 简洁,避免冗余
|
||||
|
||||
### 删除规范时
|
||||
|
||||
1. 删除 `docs/` 下的详细文档
|
||||
2. 删除 AGENTS.md 中的相关章节
|
||||
3. 删除 README.md 中的链接
|
||||
4. 说明删除原因(在 commit message 中)
|
||||
|
||||
## Skill 规范管理
|
||||
|
||||
### 何时创建 Skill
|
||||
|
||||
当规范内容满足以下条件时,应该提取为 Skill:
|
||||
- 内容超过 50 行
|
||||
- 只在特定任务场景需要
|
||||
- 包含详细的步骤和示例
|
||||
|
||||
### Skill 文件结构
|
||||
|
||||
```
|
||||
.claude/skills/{skill-name}/
|
||||
└── SKILL.md
|
||||
```
|
||||
|
||||
### Skill 命名规范
|
||||
|
||||
- 使用小写字母和连字符
|
||||
- 名称应描述规范主题
|
||||
- 示例:`dto-standards`、`db-migration`、`api-routing`
|
||||
|
||||
### Skill Frontmatter
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: skill-name
|
||||
description: 简短描述(1-2 句话),说明何时使用此 skill
|
||||
---
|
||||
```
|
||||
|
||||
## AI 助手检查清单
|
||||
|
||||
添加/更新规范后必须:
|
||||
|
||||
1. ✅ 详细文档在 `docs/` 目录
|
||||
2. ✅ AGENTS.md 中有简短引导(≤20 行)
|
||||
3. ✅ README.md 中有文档链接
|
||||
4. ✅ 如果内容 >50 行,考虑提取为 Skill
|
||||
5. ✅ Skill 的 name 与目录名一致
|
||||
6. ✅ Skill 的 description 清晰描述触发条件
|
||||
@@ -1,246 +0,0 @@
|
||||
---
|
||||
name: dto-standards
|
||||
description: DTO 数据传输对象规范。创建或修改 DTO 文件、请求/响应结构时使用。包含 description 标签、枚举字段、验证标签等规范。
|
||||
---
|
||||
|
||||
# DTO 规范
|
||||
|
||||
**所有 DTO 文件必须遵循以下规范,这是 API 文档生成的基础。**
|
||||
|
||||
## 触发条件
|
||||
|
||||
在以下情况下必须遵守本规范:
|
||||
- 创建或修改 `internal/model/` 下的请求/响应 DTO
|
||||
- 创建 `XXXRequest`、`XXXResponse`、`XXXReq`、`XXXResp` 结构体
|
||||
- 添加或修改 API 接口的输入输出参数
|
||||
|
||||
---
|
||||
|
||||
## 必须项(MUST)
|
||||
|
||||
### 1. Description 标签规范
|
||||
|
||||
**所有字段必须使用 `description` 标签,禁止使用行内注释**
|
||||
|
||||
❌ **错误**:
|
||||
```go
|
||||
type CreateUserRequest struct {
|
||||
Username string `json:"username"` // 用户名
|
||||
Status int `json:"status"` // 状态
|
||||
}
|
||||
```
|
||||
|
||||
✅ **正确**:
|
||||
```go
|
||||
type CreateUserRequest struct {
|
||||
Username string `json:"username" description:"用户名"`
|
||||
Status int `json:"status" description:"状态 (0:禁用, 1:启用)"`
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 枚举字段:int vs string 选择
|
||||
|
||||
**必须按以下规则选择类型,禁止混用:**
|
||||
|
||||
| 场景 | 类型 | 示例 |
|
||||
|------|------|------|
|
||||
| 状态类(生命周期阶段) | `int` | 待支付→已完成→已关闭 |
|
||||
| 布尔状态(启用/禁用) | `int` | `0=禁用, 1=启用` |
|
||||
| 类型/方式类(种类) | `string` | `"wechat"`, `"single_card"` |
|
||||
| 平台/标识符类 | `string` | `"web"`, `"h5"`, `"all"` |
|
||||
|
||||
```go
|
||||
// ✅ 状态 → int
|
||||
Status int `json:"status"`
|
||||
PaymentStatus int `json:"payment_status"`
|
||||
|
||||
// ✅ 类型/方式 → string
|
||||
PaymentMethod string `json:"payment_method" validate:"required,oneof=wechat offline"`
|
||||
OrderType string `json:"order_type" validate:"required,oneof=single_card device"`
|
||||
```
|
||||
|
||||
### 3. Int 状态值约定
|
||||
|
||||
#### 3.1 通用禁用/启用
|
||||
|
||||
**必须用全局常量,禁止自定义(尤其禁止 1=启用 2=禁用 这种反向写法)**:
|
||||
|
||||
```go
|
||||
// pkg/constants/constants.go 已定义,直接使用
|
||||
StatusDisabled = 0 // 禁用
|
||||
StatusEnabled = 1 // 启用
|
||||
```
|
||||
|
||||
✅ 正确:`description:"状态 (0:禁用, 1:启用)"`
|
||||
❌ 禁止:`description:"状态 (1:启用, 2:禁用)"`
|
||||
|
||||
#### 3.2 生命周期状态
|
||||
|
||||
从 **1** 开始递增,0 不使用(避免与 Go 零值混淆):
|
||||
|
||||
```go
|
||||
const (
|
||||
RechargeStatusPending = 1 // 待支付
|
||||
RechargeStatusPaid = 2 // 已支付
|
||||
RechargeStatusCompleted = 3 // 已完成
|
||||
RechargeStatusClosed = 4 // 已关闭
|
||||
)
|
||||
```
|
||||
|
||||
### 4. 枚举列表必须从 constants 原文抄写
|
||||
|
||||
**DTO description 的枚举列表必须与 `pkg/constants/` 定义完全一致,不可凭记忆填写。**
|
||||
|
||||
操作步骤:
|
||||
1. 先查/定义 `pkg/constants/` 中的枚举常量
|
||||
2. 将常量注释**原文抄写**到 description
|
||||
|
||||
```go
|
||||
// constants.go 中:
|
||||
RechargeStatusPending = 1 // 待支付
|
||||
RechargeStatusPaid = 2 // 已支付
|
||||
RechargeStatusCompleted = 3 // 已完成
|
||||
RechargeStatusClosed = 4 // 已关闭
|
||||
RechargeStatusRefunded = 5 // 已退款
|
||||
|
||||
// DTO description 从上面抄:
|
||||
Status int `json:"status" description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款)"`
|
||||
```
|
||||
|
||||
❌ 禁止(description 与 constants 不一致,是历史 bug 的根因):
|
||||
```go
|
||||
// constants 说 3=已完成,description 却写 3:已取消
|
||||
Status int `json:"status" description:"状态 (1:待支付, 2:已完成, 3:已取消)"`
|
||||
```
|
||||
|
||||
### 5. description 格式标准
|
||||
|
||||
**统一格式**:`字段含义 (值1:中文含义1, 值2:中文含义2)`
|
||||
|
||||
- 值与含义之间用**冒号** `:`(禁止用等号 `=`)
|
||||
- 多个值之间用**逗号加空格** `, `
|
||||
- 含义必须是**中文**
|
||||
|
||||
```go
|
||||
// ✅ 统一格式
|
||||
Status int `description:"状态 (1:待支付, 2:已支付, 3:已完成)"`
|
||||
Platform string `description:"适用端口 (all:全部, web:Web后台, h5:H5端)"`
|
||||
|
||||
// ❌ 格式混乱
|
||||
Status int `description:"状态 (0=禁用, 1=启用)"` // 用等号
|
||||
Status int `description:"0=禁用 1=启用"` // 无括号无逗号
|
||||
```
|
||||
|
||||
### 6. Response DTO 的状态字段必须同时返回 int 和 text
|
||||
|
||||
**所有 Response DTO 中的 int 状态字段,必须同时提供对应的 `_name` 文字字段。**
|
||||
|
||||
原因:防止前端维护映射表出错(历史上已有因此产生 bug 的案例)。
|
||||
|
||||
```go
|
||||
// ✅ Response DTO 标准写法
|
||||
type XxxResponse struct {
|
||||
Status int `json:"status" description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款)"`
|
||||
StatusName string `json:"status_name" description:"状态名称(中文)"`
|
||||
}
|
||||
|
||||
// toResponse 函数中赋值
|
||||
func rechargeStatusName(status int) string {
|
||||
switch status {
|
||||
case constants.RechargeStatusPending:
|
||||
return "待支付"
|
||||
case constants.RechargeStatusCompleted:
|
||||
return "已完成"
|
||||
// ...
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
字段命名约定:`status` → `status_name`,`payment_status` → `payment_status_name`
|
||||
|
||||
**例外**:Request DTO(查询过滤、创建请求)不需要 `_name` 字段。
|
||||
|
||||
### 7. 验证标签与 OpenAPI 标签一致
|
||||
|
||||
```go
|
||||
Username string `json:"username" validate:"required,min=3,max=50" required:"true" minLength:"3" maxLength:"50" description:"用户名"`
|
||||
```
|
||||
|
||||
| validate 标签 | OpenAPI 标签 |
|
||||
|--------------|--------------|
|
||||
| `required` | `required:"true"` |
|
||||
| `min=N,max=M`(数值) | `minimum:"N" maximum:"M"` |
|
||||
| `min=N,max=M`(字符串) | `minLength:"N" maxLength:"M"` |
|
||||
| `oneof=A B C` | description 中说明枚举值 |
|
||||
|
||||
### 8. 请求参数类型标签
|
||||
|
||||
```go
|
||||
// Query 参数
|
||||
type ListRequest struct {
|
||||
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
|
||||
Status *int `json:"status" query:"status" validate:"omitempty,min=1,max=4" minimum:"1" maximum:"4" description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭)"`
|
||||
}
|
||||
|
||||
// Path 参数
|
||||
type IDReq struct {
|
||||
ID uint `path:"id" description:"ID" required:"true"`
|
||||
}
|
||||
```
|
||||
|
||||
### 9. 响应 DTO 完整性
|
||||
|
||||
```go
|
||||
type AccountResponse struct {
|
||||
ID uint `json:"id" description:"账号ID"`
|
||||
Username string `json:"username" description:"用户名"`
|
||||
UserType int `json:"user_type" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"`
|
||||
Status int `json:"status" description:"状态 (0:禁用, 1:启用)"`
|
||||
StatusName string `json:"status_name" description:"状态名称(中文)"`
|
||||
CreatedAt string `json:"created_at" description:"创建时间"`
|
||||
UpdatedAt string `json:"updated_at" description:"更新时间"`
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AI 助手必须执行的检查
|
||||
|
||||
**在创建或修改任何 DTO 文件后,必须执行以下检查:**
|
||||
|
||||
1. ✅ 所有字段有 `description` 标签(无行内注释)
|
||||
2. ✅ 枚举类型选择正确(状态用 int,类型/方式用 string)
|
||||
3. ✅ 禁用/启用使用 `0=禁用, 1=启用`(禁止 1=启用 2=禁用)
|
||||
4. ✅ description 枚举列表已从 `pkg/constants/` 原文抄写,无遗漏
|
||||
5. ✅ description 格式统一(冒号 `:`,括号,逗号)
|
||||
6. ✅ Response DTO 有 `_name` 伴生字段
|
||||
7. ✅ validate 标签与 OpenAPI 标签一致
|
||||
8. ✅ 重新生成 OpenAPI 文档验证:`go run cmd/gendocs/main.go`
|
||||
|
||||
**完整枚举规范**: 参见 [`docs/enum-status-standards.md`](../../docs/enum-status-standards.md)
|
||||
|
||||
---
|
||||
|
||||
## 常见枚举字段标准值
|
||||
|
||||
```go
|
||||
// 用户类型(从 constants.UserType* 抄)
|
||||
description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"
|
||||
|
||||
// 通用启用/禁用(从 constants.StatusEnabled/Disabled 抄)
|
||||
description:"状态 (0:禁用, 1:启用)"
|
||||
|
||||
// 充值状态(从 constants.RechargeStatus* 抄)
|
||||
description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款)"
|
||||
|
||||
// 权限类型
|
||||
description:"权限类型 (1:菜单, 2:按钮)"
|
||||
|
||||
// 适用端口
|
||||
description:"适用端口 (all:全部, web:Web后台, h5:H5端)"
|
||||
|
||||
// 店铺层级
|
||||
description:"店铺层级 (1-7级)"
|
||||
```
|
||||
@@ -1,777 +0,0 @@
|
||||
---
|
||||
name: hurl-test
|
||||
description: Hurl 接口测试生成器。用户描述要测试的接口或业务流程,自动探索代码、确认需求、生成完整的 .hurl 测试文件(含 DTO 驱动的字段完整性断言)。触发词:测试、hurl、写测试、接口测试。
|
||||
---
|
||||
|
||||
# Hurl 接口测试生成器
|
||||
|
||||
**用户描述要测试什么,你来读代码、问确认、生成 .hurl 文件。**
|
||||
|
||||
适用于任何后端项目(Go / Python / Node / Java 等),不预设框架和目录结构。
|
||||
|
||||
---
|
||||
|
||||
## 触发条件
|
||||
|
||||
以下情况必须使用本 Skill:
|
||||
- 用户说"测试 XX 接口"、"写 hurl 测试"、"给 XX 加测试"
|
||||
- 用户说"测试 XX 流程"、"测试 XX 的业务逻辑"
|
||||
- 用户说"验证 XX 接口的字段"、"测试接口契约"
|
||||
- 用户提到 hurl、.hurl、接口测试、集成测试、冒烟测试
|
||||
|
||||
---
|
||||
|
||||
## 四阶段工作流(必须按顺序执行)
|
||||
|
||||
```
|
||||
Phase 1: 探索 → Phase 2: 确认 → Phase 3: 生成 → Phase 4: 验证
|
||||
读代码搞清楚 展示给用户确认 输出 .hurl 文件 语法检查 + 试跑
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 1: 探索(Explore)
|
||||
|
||||
**目标:读代码,搞清楚项目约定 + 涉及的接口 + 字段 + 依赖。**
|
||||
|
||||
#### 1.1 项目画像(首次使用时必须执行,后续复用)
|
||||
|
||||
首次为项目生成 Hurl 测试时,先回答以下问题(通过读代码,不要猜):
|
||||
|
||||
| 问题 | 怎么找 |
|
||||
|------|--------|
|
||||
| **语言/框架** | 看 go.mod / package.json / requirements.txt / pom.xml |
|
||||
| **路由注册在哪** | 搜索 `router`、`app.Get`、`@GetMapping`、`@app.route` 等关键词 |
|
||||
| **请求/响应 schema 定义在哪** | 搜索 DTO / schema / serializer / model 目录,看 json tag 或装饰器 |
|
||||
| **统一响应格式是什么** | 找 response helper 文件(如 `response.go`、`response.py`),记录 JSON 结构 |
|
||||
| **认证方式是什么** | 找 auth middleware,确定是 Bearer Token / Cookie / API Key / Basic Auth |
|
||||
| **登录接口是什么** | 找登录 handler,记录路径、请求体、响应中 token 的位置 |
|
||||
| **分页格式是什么** | 找列表接口的响应结构,记录 items/total/page 等字段名 |
|
||||
| **已有 hurl 测试吗** | 搜索 `*.hurl` 文件,复用已有的约定 |
|
||||
|
||||
将画像结果**写入 `tests/hurl/.project-profile.md` 文件持久化保存**。
|
||||
|
||||
#### 画像持久化(关键机制)
|
||||
|
||||
**首次使用时**:完成 1.1 探索后,将画像写入 `tests/hurl/.project-profile.md`,格式如下:
|
||||
|
||||
```markdown
|
||||
# 项目画像(Hurl 测试自动生成用)
|
||||
<!-- 由 hurl-test skill 自动生成,请勿手动修改 -->
|
||||
<!-- 如需刷新,删除此文件后重新运行 skill -->
|
||||
|
||||
## 技术栈
|
||||
- 语言: Go 1.25
|
||||
- 框架: Fiber v2
|
||||
- ORM: GORM
|
||||
|
||||
## 路由定义位置
|
||||
- 路由注册入口: internal/routes/routes.go
|
||||
- 按模块拆分: internal/routes/{module}.go
|
||||
- 路由注册函数: Register(router, doc, basePath, method, path, handler, spec)
|
||||
|
||||
## Schema 定义位置
|
||||
- DTO 目录: internal/model/dto/
|
||||
- 命名规则: {module}_dto.go
|
||||
- 字段标签: json / validate / description
|
||||
|
||||
## 统一响应格式
|
||||
{code: int, msg: string, data: any, timestamp: string(RFC3339)}
|
||||
- 成功: code=0, msg="success"
|
||||
- 错误: code!=0
|
||||
|
||||
## 分页格式
|
||||
{items: [], total: int, page: int, size: int}
|
||||
- 包裹在 data 字段内: $.data.items / $.data.total
|
||||
|
||||
## 认证方式
|
||||
- 后台: POST /api/auth/admin-login → $.data.access_token → Authorization: Bearer {token}
|
||||
- C端: JWT → Authorization: Bearer {token}
|
||||
|
||||
## 默认测试账号
|
||||
- 用户名: admin
|
||||
- 密码: Admin@123456
|
||||
|
||||
## 服务端口
|
||||
- 默认: 3000
|
||||
```
|
||||
|
||||
**后续使用时**:检查 `tests/hurl/.project-profile.md` 是否存在:
|
||||
- **存在** → 直接读取,跳过 1.1 的探索步骤,节省时间
|
||||
- **不存在** → 执行 1.1 完整探索,然后生成此文件
|
||||
- **用户说"刷新画像"** → 删除旧文件,重新执行 1.1
|
||||
|
||||
#### 1.2 找接口定义
|
||||
|
||||
根据用户要测的模块,定位路由注册代码,提取:
|
||||
|
||||
- **HTTP 方法**(GET / POST / PUT / DELETE / PATCH)
|
||||
- **路由路径**(含路径参数格式,如 `/users/:id` 或 `/users/{id}`)
|
||||
- **接口说明**(注释、Summary、装饰器描述)
|
||||
- **是否需要认证**
|
||||
- **请求 schema 类型名**(Input / Request DTO)
|
||||
- **响应 schema 类型名**(Output / Response DTO)
|
||||
|
||||
#### 1.3 读 schema 定义(DTO / struct / class / type)
|
||||
|
||||
定位请求和响应的 schema 定义文件,提取每个字段的:
|
||||
|
||||
- **字段名**:JSON 序列化后的名称(json tag / @JsonProperty / serializer field)
|
||||
- **语言类型**:string / int / bool / 数组 / 嵌套对象 / 可空等
|
||||
- **是否必填**:validate tag / required 装饰器 / 非空标注
|
||||
- **是否可空**:指针类型 / Optional / nullable
|
||||
- **是否参与序列化**:`json:"-"` / @JsonIgnore / exclude
|
||||
- **是否 omitempty**:`json:",omitempty"` / 条件序列化
|
||||
- **字段描述**:description tag / docstring / 注释
|
||||
|
||||
#### 1.4 识别业务依赖
|
||||
|
||||
读 service / business logic 层,识别:
|
||||
|
||||
- 创建操作需要哪些前置数据(如创建订单需要先有商品和用户)
|
||||
- 是否有唯一性约束(如用户名不能重复)
|
||||
- 是否依赖外部服务(支付网关、短信、OAuth 等)
|
||||
- 业务流转逻辑(状态机、级联操作)
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: 确认(Clarify)
|
||||
|
||||
**目标:向用户展示发现的内容,确认模糊点。不要闷头生成。**
|
||||
|
||||
#### 2.1 必须展示的内容
|
||||
|
||||
```
|
||||
我梳理了相关代码,发现以下信息:
|
||||
|
||||
📋 涉及接口:
|
||||
- [方法] [路径] - [说明](认证: 是/否)
|
||||
- ...
|
||||
|
||||
📦 响应字段(基于 {SchemaName}):
|
||||
- [字段名]: [类型] - [说明]
|
||||
- ...(共 N 个字段,将全部生成断言)
|
||||
|
||||
🔗 依赖关系:
|
||||
- [创建 X 需要先创建 Y]
|
||||
- ...
|
||||
|
||||
⚠️ 特殊情况:
|
||||
- [涉及外部服务 / 文件上传 / 特殊认证等]
|
||||
```
|
||||
|
||||
#### 2.2 按需确认(只问有歧义的)
|
||||
|
||||
| 场景 | 要问的 |
|
||||
|------|--------|
|
||||
| 流程范围不明确 | "要测到哪一步?" |
|
||||
| 多种用户角色 | "用哪种身份测?" |
|
||||
| 是否测异常 | "需要包含异常 case 吗?(参数校验失败、权限不足等)" |
|
||||
| 是否测数据隔离 | "需要验证不同用户间数据不可见吗?" |
|
||||
| 涉及第三方 | "XX 部分怎么处理?绕过 / 模拟回调 / 跳过?" |
|
||||
| 前置数据来源 | "XX 依赖数据是通过 API 创建还是假设已存在?" |
|
||||
|
||||
**如果用户说"越完整越好"或"都要"→ 默认全部包含,不再追问。**
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: 生成(Generate)
|
||||
|
||||
**目标:生成完整的 .hurl 文件,字段断言基于 schema 代码,不能编造。**
|
||||
|
||||
#### 3.1 文件头注释
|
||||
|
||||
```hurl
|
||||
# ============================================================
|
||||
# 测试:{测试名称}
|
||||
# 生成时间:{日期}
|
||||
# 涉及模块:{module1, module2, ...}
|
||||
# 涉及接口:{N} 个
|
||||
# 断言数量:{N} 条
|
||||
# 前置条件:{服务运行 + 必要的前置条件}
|
||||
# ============================================================
|
||||
# 流程:
|
||||
# 1. {步骤描述}
|
||||
# 2. {步骤描述}
|
||||
# ...
|
||||
# ============================================================
|
||||
```
|
||||
|
||||
#### 3.2 请求生成规则
|
||||
|
||||
**认证**:
|
||||
|
||||
- 根据 Phase 1 画像中的登录接口和 token 位置生成
|
||||
- token 必须通过 `[Captures]` 捕获,后续请求引用
|
||||
- 如果是 Cookie 认证,用 `[Cookies]` 或 cookie capture
|
||||
|
||||
**CRUD 标准模式**:
|
||||
|
||||
| 操作 | 生成要求 |
|
||||
|------|---------|
|
||||
| **创建(POST)** | capture 返回的 ID;唯一字段用 `{{newUuid}}` 防冲突 |
|
||||
| **查询详情(GET)** | **逐字段断言**(类型 + 值,见 3.3) |
|
||||
| **查询列表(GET)** | 分页结构断言 + items[0] 逐字段断言 |
|
||||
| **修改(PUT/PATCH)** | 修改后**紧跟一个 GET 验证修改生效** |
|
||||
| **删除(DELETE)** | 删除后**紧跟一个 GET 验证已删除** |
|
||||
|
||||
**业务流程模式**:
|
||||
|
||||
- 按用户描述的流程顺序编排请求
|
||||
- 上一步的输出(ID、状态等)通过 `[Captures]` 传给下一步
|
||||
- 关键步骤加中间状态验证(如创建订单后验证状态为"待支付")
|
||||
|
||||
#### 3.3 schema 到断言的映射
|
||||
|
||||
读到 schema 字段后,按以下规则生成 jsonpath 断言:
|
||||
|
||||
**通用类型映射(所有语言)**:
|
||||
|
||||
| Schema 类型特征 | Hurl 断言 |
|
||||
|----------------|-----------|
|
||||
| 字符串(string / str / String) | `isString` |
|
||||
| 整数(int / integer / long / Int) | `isInteger` |
|
||||
| 浮点(float / double / decimal / Float) | `isNumber` |
|
||||
| 布尔(bool / boolean / Boolean) | `isBoolean` |
|
||||
| 数组 / 列表([] / List / Array) | `isList` |
|
||||
| 嵌套对象(struct / class / dict / object) | `isObject`,并递归检查子字段 |
|
||||
| 可空类型(指针 / Optional / nullable) | `exists`(不强制类型,因为可能是 null) |
|
||||
| 不参与序列化(json:"-" / @JsonIgnore / exclude=True) | **跳过,不生成断言** |
|
||||
| 条件序列化(omitempty / if not None) | `exists` 或不生成(取决于场景) |
|
||||
|
||||
**Go 特定映射**:
|
||||
|
||||
| Go 类型 | Hurl 断言 |
|
||||
|---------|-----------|
|
||||
| `string` | `isString` |
|
||||
| `int`, `int8/16/32/64`, `uint`, `uint8/16/32/64` | `isInteger` |
|
||||
| `float32`, `float64` | `isNumber` |
|
||||
| `bool` | `isBoolean` |
|
||||
| `[]T` | `isList` |
|
||||
| `*string`, `*int`, `*uint` 等指针 | `exists` |
|
||||
| `time.Time` | `isString`(通常序列化为字符串) |
|
||||
| `map[string]any` | `isObject` |
|
||||
|
||||
**Python 特定映射(Pydantic / Django / FastAPI)**:
|
||||
|
||||
| Python 类型 | Hurl 断言 |
|
||||
|------------|-----------|
|
||||
| `str` | `isString` |
|
||||
| `int` | `isInteger` |
|
||||
| `float`, `Decimal` | `isNumber` |
|
||||
| `bool` | `isBoolean` |
|
||||
| `list[T]`, `List[T]` | `isList` |
|
||||
| `Optional[T]`, `T | None` | `exists` |
|
||||
| `dict`, `Dict` | `isObject` |
|
||||
| `datetime`, `date` | `isString` |
|
||||
|
||||
**TypeScript/JavaScript 特定映射**:
|
||||
|
||||
| TS/JS 类型 | Hurl 断言 |
|
||||
|-----------|-----------|
|
||||
| `string` | `isString` |
|
||||
| `number`(整数上下文) | `isInteger` |
|
||||
| `number`(通用) | `isNumber` |
|
||||
| `boolean` | `isBoolean` |
|
||||
| `T[]`, `Array<T>` | `isList` |
|
||||
| `T \| null`, `T \| undefined` | `exists` |
|
||||
| `object`, `Record<>` | `isObject` |
|
||||
| `Date` | `isString` |
|
||||
|
||||
**Java 特定映射**:
|
||||
|
||||
| Java 类型 | Hurl 断言 |
|
||||
|----------|-----------|
|
||||
| `String` | `isString` |
|
||||
| `Integer`, `Long`, `int`, `long` | `isInteger` |
|
||||
| `Double`, `Float`, `BigDecimal` | `isNumber` |
|
||||
| `Boolean`, `boolean` | `isBoolean` |
|
||||
| `List<T>` | `isList` |
|
||||
| `@Nullable`, `Optional<T>` | `exists` |
|
||||
| `Map<K,V>` | `isObject` |
|
||||
| `LocalDateTime`, `Instant` | `isString` |
|
||||
|
||||
#### 3.4 统一响应格式断言
|
||||
|
||||
根据 Phase 1 画像中发现的统一响应格式,为**每个成功响应**添加格式断言。
|
||||
|
||||
示例:如果项目的统一格式是 `{code, msg, data, timestamp}`:
|
||||
|
||||
```hurl
|
||||
[Asserts]
|
||||
jsonpath "$.code" == 0
|
||||
jsonpath "$.msg" == "success"
|
||||
jsonpath "$.timestamp" isIsoDate
|
||||
```
|
||||
|
||||
示例:如果项目的格式是 `{status, message, result}`:
|
||||
|
||||
```hurl
|
||||
[Asserts]
|
||||
jsonpath "$.status" == "ok"
|
||||
jsonpath "$.message" isString
|
||||
```
|
||||
|
||||
示例:如果项目无统一包装,直接返回数据:
|
||||
|
||||
```hurl
|
||||
[Asserts]
|
||||
# 直接断言业务字段
|
||||
jsonpath "$.id" isInteger
|
||||
jsonpath "$.name" isString
|
||||
```
|
||||
|
||||
**不要假设响应格式,必须从代码中确认。**
|
||||
|
||||
#### 3.5 分页断言
|
||||
|
||||
根据 Phase 1 画像中发现的分页结构生成。
|
||||
|
||||
示例:如果是 `{items, total, page, size}` 格式:
|
||||
|
||||
```hurl
|
||||
jsonpath "$.data.items" isList
|
||||
jsonpath "$.data.total" isInteger
|
||||
jsonpath "$.data.total" >= 1
|
||||
jsonpath "$.data.page" isInteger
|
||||
jsonpath "$.data.size" isInteger
|
||||
# items 内元素逐字段断言
|
||||
jsonpath "$.data.items[0].{field}" {type_assert}
|
||||
```
|
||||
|
||||
示例:如果是 `{results, count, next, previous}` 格式(Django 风格):
|
||||
|
||||
```hurl
|
||||
jsonpath "$.results" isList
|
||||
jsonpath "$.count" isInteger
|
||||
jsonpath "$.count" >= 1
|
||||
# results 内元素逐字段断言
|
||||
jsonpath "$.results[0].{field}" {type_assert}
|
||||
```
|
||||
|
||||
**根据实际代码调整字段名,不硬编码。**
|
||||
|
||||
#### 3.6 异常 Case 模板
|
||||
|
||||
**参数校验失败**:
|
||||
|
||||
```hurl
|
||||
# ── 异常:参数校验失败 ──
|
||||
POST {{base_url}}/{path}
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
{
|
||||
"required_field": ""
|
||||
}
|
||||
HTTP {expected_error_status}
|
||||
[Asserts]
|
||||
# 断言错误响应格式(根据项目约定调整)
|
||||
```
|
||||
|
||||
> HTTP 状态码根据项目实际返回确定:有的项目错误也返回 200 + 业务错误码,有的返回 400/422。
|
||||
|
||||
**未认证访问**:
|
||||
|
||||
```hurl
|
||||
# ── 异常:未认证访问 ──
|
||||
GET {{base_url}}/{protected_path}
|
||||
HTTP {expected_unauth_status}
|
||||
```
|
||||
|
||||
**越权访问**(如果用户要求):
|
||||
|
||||
```hurl
|
||||
# ── 异常:用户 B 不能访问用户 A 的资源 ──
|
||||
GET {{base_url}}/{path}/{{user_a_resource_id}}
|
||||
Authorization: Bearer {{user_b_token}}
|
||||
HTTP {expected_forbidden_status}
|
||||
```
|
||||
|
||||
#### 3.7 特殊场景处理
|
||||
|
||||
| 场景 | 处理策略 |
|
||||
|------|---------|
|
||||
| **短信/邮件验证码** | 建议服务端加 test_mode 开关,固定验证码写入 env 文件;注释提醒用户 |
|
||||
| **第三方支付** | 优先用项目内部支付方式(如钱包支付);如需测回调,直接 POST 回调接口模拟 |
|
||||
| **OAuth 登录(微信/Google/GitHub)** | 建议服务端加 test_mode 支持直接传 openid/email;注释提醒用户 |
|
||||
| **文件上传** | 用 Hurl 的 `[Multipart]` 语法 + testdata 目录下的样本文件 |
|
||||
| **外部 API 依赖** | 只测参数校验和错误响应格式,不断言业务结果;注释说明依赖 |
|
||||
| **WebSocket** | Hurl 不支持,注释说明跳过 |
|
||||
| **异步任务结果** | 用 Hurl 的 `retry` + `retry-interval` 轮询直到状态变更 |
|
||||
|
||||
异步轮询示例:
|
||||
|
||||
```hurl
|
||||
# 等待异步任务完成(最多重试 10 次,间隔 500ms)
|
||||
GET {{base_url}}/{path}/{{task_id}}
|
||||
Authorization: Bearer {{token}}
|
||||
[Options]
|
||||
retry: 10
|
||||
retry-interval: 500ms
|
||||
HTTP 200
|
||||
[Asserts]
|
||||
jsonpath "$.data.status" == "completed"
|
||||
```
|
||||
|
||||
#### 3.8 文件输出
|
||||
|
||||
**目录结构**(首次使用时创建,如不存在):
|
||||
|
||||
```
|
||||
tests/hurl/
|
||||
├── env/
|
||||
│ └── dev.env # 环境变量
|
||||
├── testdata/ # 测试用的样本文件
|
||||
├── flows/ # 业务流程测试
|
||||
├── modules/ # 按模块的接口测试
|
||||
│ └── {module}/
|
||||
│ └── 01-crud.hurl
|
||||
├── negative/ # 异常/边界测试
|
||||
├── contract/ # 接口契约验证
|
||||
└── Makefile # 快捷命令
|
||||
```
|
||||
|
||||
文件放置规则:
|
||||
|
||||
| 用户描述 | 输出路径 |
|
||||
|---------|---------|
|
||||
| 测试 XX 流程 / 业务流程 | `tests/hurl/flows/{flow-name}.hurl` |
|
||||
| 测试 XX 模块的 CRUD / 接口 | `tests/hurl/modules/{module}/01-crud.hurl` |
|
||||
| 测试异常/边界/权限 | `tests/hurl/negative/{name}.hurl` |
|
||||
| 测试接口契约/字段对齐 | `tests/hurl/contract/{name}.hurl` |
|
||||
|
||||
**如果项目已有 hurl 测试目录结构,沿用已有约定,不要另起炉灶。**
|
||||
|
||||
#### 3.9 env 和 Makefile
|
||||
|
||||
**env/dev.env**(首次创建时生成,内容基于 Phase 1 画像):
|
||||
|
||||
```properties
|
||||
# 服务地址
|
||||
base_url=http://localhost:{port}
|
||||
|
||||
# 认证信息(根据项目实际填写)
|
||||
admin_username={默认用户名}
|
||||
admin_password={默认密码}
|
||||
|
||||
# 测试模式变量(如果有特殊场景)
|
||||
# test_sms_code=888888
|
||||
# test_openid=test_openid_001
|
||||
```
|
||||
|
||||
**Makefile**(首次创建时生成):
|
||||
|
||||
```makefile
|
||||
SHELL := /bin/bash
|
||||
ENV ?= dev
|
||||
HURL_OPTS := --variables-file env/$(ENV).env --test
|
||||
|
||||
.PHONY: test test-flows test-modules test-negative report
|
||||
|
||||
test: ## 运行所有测试
|
||||
hurl $(HURL_OPTS) .
|
||||
|
||||
test-flows: ## 运行业务流程测试
|
||||
hurl $(HURL_OPTS) flows/
|
||||
|
||||
test-modules: ## 运行模块接口测试
|
||||
hurl $(HURL_OPTS) modules/
|
||||
|
||||
test-negative: ## 运行异常测试
|
||||
hurl $(HURL_OPTS) negative/
|
||||
|
||||
report: ## 生成 HTML 报告
|
||||
hurl $(HURL_OPTS) --report-html build/report/ .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: 验证(Verify)
|
||||
|
||||
**目标:确保生成的 .hurl 文件语法正确、可运行。**
|
||||
|
||||
#### 4.1 语法自检
|
||||
|
||||
- [ ] 每个请求之间有空行分隔
|
||||
- [ ] `[Captures]` 和 `[Asserts]` 拼写正确(大小写敏感)
|
||||
- [ ] 所有 `{{变量}}` 引用都有来源(env 文件定义 或 上游 `[Captures]`)
|
||||
- [ ] JSON body 无尾逗号、格式正确
|
||||
- [ ] `HTTP {status}` 在请求之后、`[Captures]` / `[Asserts]` 之前
|
||||
- [ ] 请求和断言之间没有多余空行(`HTTP` 行必须紧跟请求)
|
||||
- [ ] 文件上传路径相对于 hurl 文件位置正确
|
||||
|
||||
#### 4.2 运行测试
|
||||
|
||||
```bash
|
||||
hurl --variables-file tests/hurl/env/dev.env --test tests/hurl/{生成的文件}
|
||||
```
|
||||
|
||||
- 服务在跑 → 执行,如有失败分析修正
|
||||
- 服务没跑 → 跳过,告知用户手动验证命令
|
||||
|
||||
---
|
||||
|
||||
## 红线规则
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| **不跳过 Phase 1** | 必须读代码确认接口路径和字段,不能凭记忆或猜测 |
|
||||
| **不跳过 Phase 2** | 必须向用户展示发现的接口和字段,确认后再生成 |
|
||||
| **不编造字段** | 所有断言的字段名必须来自实际 schema 代码 |
|
||||
| **不编造路径** | 所有接口路径必须来自实际路由代码 |
|
||||
| **不遗漏字段** | schema 中每个参与序列化的字段都必须有对应断言 |
|
||||
| **不硬编码 ID** | 所有依赖的 ID 通过 `[Captures]` 从上游请求获取 |
|
||||
| **不假设响应格式** | 统一响应结构必须从代码中确认,不同项目格式不同 |
|
||||
| **唯一值防冲突** | 创建类请求的唯一字段使用 `{{newUuid}}` 或 `{{newDate}}` |
|
||||
| **自给自足** | 每个 .hurl 文件自己创建测试数据,不依赖外部数据准备 |
|
||||
|
||||
---
|
||||
|
||||
## AI 助手检查清单
|
||||
|
||||
生成 .hurl 文件后自检:
|
||||
|
||||
1. ✅ 文件头注释包含流程描述和前置条件
|
||||
2. ✅ 认证步骤正确 capture 了 token / cookie
|
||||
3. ✅ 所有依赖数据通过 API 链式创建(自给自足)
|
||||
4. ✅ 每个成功响应断言了项目的统一响应格式
|
||||
5. ✅ 查询详情接口**逐字段断言**(类型 + 值,基于 schema)
|
||||
6. ✅ 分页接口断言了分页结构 + 第一条记录的字段
|
||||
7. ✅ 修改操作后紧跟 GET 验证修改生效
|
||||
8. ✅ 删除操作后紧跟 GET 验证已删除
|
||||
9. ✅ 唯一字段使用了 `{{newUuid}}`
|
||||
10. ✅ `{{变量}}` 引用无悬空(都有 env 或 capture 来源)
|
||||
11. ✅ 文件放在了正确的目录位置
|
||||
12. ✅ 特殊场景有明确的处理策略和注释提醒
|
||||
|
||||
---
|
||||
|
||||
## 附录:Hurl 语法速查
|
||||
|
||||
**生成 .hurl 文件时必须参照本速查,不可凭记忆编造语法。**
|
||||
|
||||
### 文件结构
|
||||
|
||||
一个 .hurl 文件由多个 entry 组成,每个 entry = 请求 + 可选响应:
|
||||
|
||||
```
|
||||
请求1
|
||||
响应1(可选)
|
||||
|
||||
请求2
|
||||
响应2(可选)
|
||||
```
|
||||
|
||||
entry 之间用空行分隔。
|
||||
|
||||
### 请求格式
|
||||
|
||||
```hurl
|
||||
METHOD URL
|
||||
Header1: value1
|
||||
Header2: value2
|
||||
[Options]
|
||||
key: value
|
||||
[Query]
|
||||
param1: value1
|
||||
[Form]
|
||||
field1: value1
|
||||
[Multipart]
|
||||
file1: file,path/to/file;
|
||||
[BasicAuth]
|
||||
username: password
|
||||
[Cookies]
|
||||
name: value
|
||||
BODY(JSON / XML / multiline string / file)
|
||||
```
|
||||
|
||||
**规则**:
|
||||
- Method + URL 是第一行,必须
|
||||
- Headers 紧跟 URL 之后(无 section 标记)
|
||||
- Sections(`[Query]`、`[Form]`、`[Options]` 等)顺序任意
|
||||
- Body 必须在最后
|
||||
- JSON body 直接写 `{ }` 即可,自动设置 Content-Type: application/json
|
||||
|
||||
### 响应格式
|
||||
|
||||
```hurl
|
||||
HTTP {status_code}
|
||||
Header1: expected_value1
|
||||
[Captures]
|
||||
var_name: jsonpath "$.path"
|
||||
[Asserts]
|
||||
jsonpath "$.field" == "value"
|
||||
```
|
||||
|
||||
**规则**:
|
||||
- `HTTP {status}` 紧跟请求之后(中间不能有空行)
|
||||
- `HTTP *` 表示不检查状态码
|
||||
- Headers 检查紧跟 HTTP 行之后
|
||||
- `[Captures]` 和 `[Asserts]` 顺序任意
|
||||
|
||||
### 变量和模板
|
||||
|
||||
```hurl
|
||||
# 引用变量(从 env 文件、命令行或上游 capture 获取)
|
||||
GET {{base_url}}/api/users/{{user_id}}
|
||||
|
||||
# 内置函数
|
||||
POST {{base_url}}/api/users
|
||||
{
|
||||
"email": "{{newUuid}}@test.com",
|
||||
"created_at": "{{newDate}}"
|
||||
}
|
||||
```
|
||||
|
||||
可用函数:
|
||||
- `{{newUuid}}` — 生成 UUID v4
|
||||
- `{{newDate}}` — 生成 RFC 3339 UTC 时间戳
|
||||
|
||||
### Capture 语法
|
||||
|
||||
```hurl
|
||||
[Captures]
|
||||
# JSONPath
|
||||
token: jsonpath "$.data.access_token"
|
||||
user_id: jsonpath "$.data.id"
|
||||
first_item: jsonpath "$.items[0].name"
|
||||
|
||||
# Header
|
||||
location: header "Location"
|
||||
|
||||
# Cookie
|
||||
session: cookie "SESSIONID"
|
||||
|
||||
# Body(整个响应体作为字符串)
|
||||
full_body: body
|
||||
|
||||
# Status code
|
||||
code: status
|
||||
|
||||
# 正则表达式
|
||||
csrf: regex "name=\"csrf\" value=\"([^\"]+)\""
|
||||
|
||||
# 响应时间(毫秒)
|
||||
response_time: duration
|
||||
```
|
||||
|
||||
### Assert 语法
|
||||
|
||||
```hurl
|
||||
[Asserts]
|
||||
# ── 状态码 ──
|
||||
status == 200
|
||||
status >= 200
|
||||
status < 300
|
||||
|
||||
# ── JSONPath 断言 ──
|
||||
jsonpath "$.name" == "Alice" # 等于
|
||||
jsonpath "$.name" != "Bob" # 不等于
|
||||
jsonpath "$.age" > 18 # 大于
|
||||
jsonpath "$.age" >= 18 # 大于等于
|
||||
jsonpath "$.count" < 100 # 小于
|
||||
jsonpath "$.items" count == 5 # 集合长度
|
||||
jsonpath "$.name" startsWith "Al" # 前缀
|
||||
jsonpath "$.name" endsWith "ce" # 后缀
|
||||
jsonpath "$.name" contains "lic" # 包含
|
||||
jsonpath "$.date" matches /\\d{4}-\\d{2}-\\d{2}/ # 正则
|
||||
|
||||
# ── 类型断言 ──
|
||||
jsonpath "$.name" isString
|
||||
jsonpath "$.age" isInteger
|
||||
jsonpath "$.score" isFloat
|
||||
jsonpath "$.count" isNumber # 整数或浮点
|
||||
jsonpath "$.active" isBoolean
|
||||
jsonpath "$.items" isList
|
||||
jsonpath "$.meta" isObject
|
||||
jsonpath "$.id" isUuid
|
||||
jsonpath "$.created_at" isIsoDate # RFC 3339 格式
|
||||
jsonpath "$.field" isEmpty # 空集合
|
||||
|
||||
# ── 存在性 ──
|
||||
jsonpath "$.field" exists
|
||||
jsonpath "$.field" not exists
|
||||
|
||||
# ── 否定 ──
|
||||
jsonpath "$.name" not contains "Bob"
|
||||
jsonpath "$.status" not == "deleted"
|
||||
|
||||
# ── Header 断言 ──
|
||||
header "Content-Type" contains "application/json"
|
||||
header "X-Request-Id" exists
|
||||
|
||||
# ── 性能 ──
|
||||
duration < 1000 # 响应时间(毫秒)
|
||||
|
||||
# ── Body 断言 ──
|
||||
body contains "Hello"
|
||||
bytes count == 1024
|
||||
```
|
||||
|
||||
### Options(逐请求配置)
|
||||
|
||||
```hurl
|
||||
GET {{base_url}}/api/task/{{task_id}}
|
||||
[Options]
|
||||
retry: 10 # 最大重试次数(-1 = 无限)
|
||||
retry-interval: 500ms # 重试间隔
|
||||
delay: 2s # 请求前等待
|
||||
location: true # 跟随重定向
|
||||
insecure: true # 允许不安全 SSL
|
||||
verbose: true # 输出详细日志
|
||||
very-verbose: true # 输出更详细日志
|
||||
skip: true # 跳过此请求
|
||||
variable: key=value # 定义变量
|
||||
HTTP 200
|
||||
```
|
||||
|
||||
### Multipart 文件上传
|
||||
|
||||
```hurl
|
||||
POST {{base_url}}/api/upload
|
||||
[Multipart]
|
||||
file: file,testdata/sample.xlsx;
|
||||
field1: value1
|
||||
# 指定 Content-Type
|
||||
file2: file,testdata/data.bin; application/octet-stream
|
||||
```
|
||||
|
||||
### 运行命令
|
||||
|
||||
```bash
|
||||
# 运行单个文件
|
||||
hurl --test file.hurl
|
||||
|
||||
# 带变量文件
|
||||
hurl --variables-file env/dev.env --test file.hurl
|
||||
|
||||
# 运行目录下所有 .hurl
|
||||
hurl --test tests/hurl/
|
||||
|
||||
# 生成 HTML 报告
|
||||
hurl --test --report-html build/report/ tests/hurl/
|
||||
|
||||
# 生成 JUnit 报告(CI 用)
|
||||
hurl --test --report-junit build/report.xml tests/hurl/
|
||||
|
||||
# 并行执行(--test 默认并行,同文件内串行)
|
||||
hurl --test --jobs 4 tests/hurl/
|
||||
|
||||
# 指定单个变量
|
||||
hurl --variable base_url=http://localhost:3000 --test file.hurl
|
||||
|
||||
# 失败后继续执行
|
||||
hurl --test --continue-on-error tests/hurl/
|
||||
```
|
||||
|
||||
### 常见错误
|
||||
|
||||
| 错误 | 原因 | 修正 |
|
||||
|------|------|------|
|
||||
| `HTTP 200` 和请求之间有空行 | 空行会被当作 entry 分隔符 | 删除空行,HTTP 行紧跟请求 |
|
||||
| JSON body 有尾逗号 | Hurl 严格解析 JSON | 删除最后一个逗号 |
|
||||
| `jsonpath` 写成 `json_path` 或 `JsonPath` | 关键字大小写敏感 | 必须小写 `jsonpath` |
|
||||
| `[Captures]` 写成 `[Capture]` | 必须是复数 | `[Captures]`、`[Asserts]`、`[Options]` |
|
||||
| 变量 `{{ var }}` 有空格 | 允许,但建议统一 | `{{var}}` 或 `{{ var }}` 都可以 |
|
||||
| `isIsoDate` 用在非 RFC 3339 格式 | 只认 `YYYY-MM-DDTHH:mm:ss` 格式 | 如果是其他格式用 `matches` |
|
||||
| `file,path;` 路径含 `..` | Hurl 禁止相对父目录 | 用 `--file-root` 或调整路径 |
|
||||
@@ -1,93 +0,0 @@
|
||||
---
|
||||
name: model-standards
|
||||
description: GORM Model 模型规范。创建或修改数据库模型时使用。包含模型结构、字段标签、TableName 实现等规范。
|
||||
---
|
||||
|
||||
# Model 模型规范
|
||||
|
||||
**创建或修改 `internal/model/` 下的数据库模型时必须遵守本规范。**
|
||||
|
||||
## 触发条件
|
||||
|
||||
在以下情况下必须遵守本规范:
|
||||
- 创建新的数据库模型
|
||||
- 修改现有模型的字段
|
||||
- 添加新的数据库表
|
||||
|
||||
## 必须遵守的模型结构
|
||||
|
||||
```go
|
||||
// ModelName 模型名称模型
|
||||
// 详细的业务说明(2-3行)
|
||||
// 特殊说明(如果有)
|
||||
type ModelName struct {
|
||||
gorm.Model // 包含 ID、CreatedAt、UpdatedAt、DeletedAt
|
||||
BaseModel `gorm:"embedded"` // 包含 Creator、Updater
|
||||
Field1 string `gorm:"column:field1;type:varchar(50);not null;comment:字段1说明" json:"field1"`
|
||||
// ... 其他字段
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (ModelName) TableName() string {
|
||||
return "tb_model_name"
|
||||
}
|
||||
```
|
||||
|
||||
## 关键要点
|
||||
|
||||
### 必须嵌入基础模型
|
||||
|
||||
- ✅ **必须**嵌入 `gorm.Model` 和 `BaseModel`
|
||||
- ❌ **禁止**手动定义 ID、CreatedAt、UpdatedAt、DeletedAt、Creator、Updater
|
||||
|
||||
### 必须添加中文注释
|
||||
|
||||
- ✅ **必须**为模型添加中文注释,说明业务用途(参考 `internal/model/iot_card.go`)
|
||||
- ✅ **必须**在每个字段的 `comment` 标签中添加中文说明
|
||||
- ✅ **必须**为导出的类型编写 godoc 格式的文档注释
|
||||
|
||||
### 必须实现 TableName
|
||||
|
||||
- ✅ **必须**实现 `TableName()` 方法
|
||||
- ✅ 表名使用 `tb_` 前缀
|
||||
|
||||
### 字段标签规范
|
||||
|
||||
- ✅ 所有字段必须显式指定 `gorm:"column:field_name"` 标签
|
||||
- ✅ 金额字段使用 `int64` 类型,单位为分
|
||||
- ✅ 时间字段使用 `*time.Time`(可空)或 `time.Time`(必填)
|
||||
- ✅ JSONB 字段需要实现 `driver.Valuer` 和 `sql.Scanner` 接口
|
||||
|
||||
## 完整示例
|
||||
|
||||
```go
|
||||
// IotCard 物联网卡模型
|
||||
// 记录物联网卡的基础信息、状态和套餐关联
|
||||
// 支持单卡和设备绑定两种使用模式
|
||||
type IotCard struct {
|
||||
gorm.Model
|
||||
BaseModel `gorm:"embedded"`
|
||||
ICCID string `gorm:"column:iccid;type:varchar(20);uniqueIndex;not null;comment:ICCID卡号" json:"iccid"`
|
||||
IMSI string `gorm:"column:imsi;type:varchar(20);comment:IMSI号" json:"imsi"`
|
||||
Status int `gorm:"column:status;type:smallint;default:0;comment:状态(0:未激活,1:已激活,2:已停机)" json:"status"`
|
||||
ActivatedAt *time.Time `gorm:"column:activated_at;comment:激活时间" json:"activated_at"`
|
||||
ShopID uint `gorm:"column:shop_id;index;comment:所属店铺ID" json:"shop_id"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (IotCard) TableName() string {
|
||||
return "tb_iot_card"
|
||||
}
|
||||
```
|
||||
|
||||
## AI 助手检查清单
|
||||
|
||||
修改模型后必须检查:
|
||||
|
||||
1. ✅ 是否嵌入了 `gorm.Model` 和 `BaseModel`
|
||||
2. ✅ 是否有 godoc 格式的模型注释
|
||||
3. ✅ 所有字段是否有 `gorm:"column:xxx"` 标签
|
||||
4. ✅ 所有字段是否有 `comment:xxx` 说明
|
||||
5. ✅ 是否实现了 `TableName()` 方法
|
||||
6. ✅ 表名是否使用 `tb_` 前缀
|
||||
7. ✅ 金额字段是否使用 `int64`(单位:分)
|
||||
@@ -1,97 +0,0 @@
|
||||
---
|
||||
name: openspec-api-contract
|
||||
description: OpenSpec API 契约规范。创建涉及接口的 OpenSpec 提案时使用。探索阶段提供业务与契约引导清单,提案文档要求 API 契约设计、错误码与完成标准等必填章节。
|
||||
---
|
||||
|
||||
# OpenSpec API 契约规范
|
||||
|
||||
**适用场景**:创建涉及 API/接口的 OpenSpec 提案时,探索和提案两个阶段均须遵守本规范。
|
||||
|
||||
---
|
||||
|
||||
## 一、探索阶段引导清单
|
||||
|
||||
在 `openspec-explore` 阶段,当内容涉及接口时,讨论必须覆盖以下所有维度。
|
||||
|
||||
### 业务与契约确认
|
||||
|
||||
**输入与输出**
|
||||
- 请求方是谁?用户类型(SuperAdmin/Platform/Agent/Enterprise/Personal)?
|
||||
- 输入参数:必填/选填字段、格式约束、参数来源(路径/查询/Body)?
|
||||
- 输出结构:哪些字段必须返回?是否需要分页?
|
||||
|
||||
**业务规则**
|
||||
- 核心业务规则与边界条件?
|
||||
- 是否涉及状态流转?状态机的完整定义?
|
||||
- 依赖外部服务时,外部异常的降级行为?
|
||||
|
||||
**权限与资源所有权**
|
||||
- 哪些用户类型可以访问?
|
||||
- 是否涉及跨用户/跨店铺/跨企业的资源访问?(需三层越权防护)
|
||||
- 资源所有权校验方式:`CanManageShop` / `CanManageEnterprise` / 自有资源?
|
||||
|
||||
**幂等性**
|
||||
- 操作类型:查询(天然幂等)/ 创建 / 更新 / 删除?
|
||||
- 写操作幂等策略:状态条件更新 / Redis 业务键防重 + 分布式锁 / 乐观锁(version)?
|
||||
- 异步任务是否需要任务锁?
|
||||
|
||||
**数据模型变更**
|
||||
- 是否需要新建表、修改现有表或数据回填?
|
||||
- 迁移策略:上线顺序、兼容旧数据的方式?
|
||||
- 是否影响 GORM Callback 自动数据权限过滤?
|
||||
|
||||
**错误码与异常语义**
|
||||
- 预期错误场景及对应错误码?
|
||||
- 错误响应是否泄露敏感信息?(参数校验失败统一返回 `CodeInvalidParam`)
|
||||
|
||||
---
|
||||
|
||||
## 二、提案文档必填章节
|
||||
|
||||
在 `openspec-propose` 生成的提案(`proposal.md` / `design.md`)中,涉及接口时以下内容**不可缺失**。
|
||||
|
||||
### API 契约设计
|
||||
|
||||
**接口定义**
|
||||
|
||||
| 项 | 内容 |
|
||||
|---|---|
|
||||
| Endpoint | `METHOD /api/{scope}/{resource}[/:id]` |
|
||||
| 请求参数 | 字段名、类型、必填/选填、说明 |
|
||||
| 响应结构 | `data` 字段的完整结构定义 |
|
||||
| 鉴权要求 | 允许的用户类型 |
|
||||
| 资源所有权 | 所有权校验方式 |
|
||||
|
||||
**列表接口额外要求**
|
||||
- 分页:`page` + `page_size`(默认 20,最大 100)
|
||||
- 排序:默认排序字段与方向
|
||||
- 过滤:支持的过滤条件列表
|
||||
|
||||
**错误码清单**
|
||||
|
||||
| 场景 | 错误码 | 说明 |
|
||||
|---|---|---|
|
||||
| 参数校验失败 | `CodeInvalidParam` | 统一返回,不泄露细节 |
|
||||
| 资源不存在/越权 | `CodeForbidden` | 不区分两者,防止信息泄露 |
|
||||
| (业务错误场景...) | (对应错误码) | (说明) |
|
||||
|
||||
### 完成标准
|
||||
|
||||
**最小验证步骤**(按顺序列出可操作的验证步骤)
|
||||
|
||||
1. (例:调用创建接口,验证返回 `code=0`)
|
||||
2. (例:查询接口确认数据存在且字段正确)
|
||||
3. (例:PostgreSQL MCP 查询确认数据库记录符合预期)
|
||||
|
||||
**影响范围说明**
|
||||
- 新增/修改的表:
|
||||
- 影响的现有接口:
|
||||
- 影响的权限与数据过滤范围:
|
||||
|
||||
---
|
||||
|
||||
## 约束(必须遵守)
|
||||
|
||||
- **优先复用现有架构与库**:不引入新依赖,错误码优先复用已有定义
|
||||
- **不做顺手重构**:提案范围严格限定在目标功能;发现可优化点,记录到 backlog 但不执行
|
||||
- **数据库设计**:禁止外键约束,禁止 GORM 关联标签,关联通过 ID 字段手动维护
|
||||
@@ -7,14 +7,14 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Implement tasks from an OpenSpec change.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
**Input**: Optionally specify a change name (e.g., `/opsx:apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
@@ -23,7 +23,7 @@ Implement tasks from an OpenSpec change.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:apply <other>`).
|
||||
|
||||
@@ -47,12 +47,29 @@ Implement tasks from an OpenSpec change.
|
||||
- Progress (total, complete, remaining)
|
||||
- Task list with status
|
||||
- Dynamic instruction based on current state
|
||||
- Optional `context`: current required project instruction input from the selected root
|
||||
- Optional `operationGuidance`: current advisory guidance for apply
|
||||
|
||||
**Handle states:**
|
||||
- If `state: "blocked"` (missing artifacts): show message, suggest using openspec-continue-change
|
||||
- If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx:continue` (if it is not installed, run `openspec status --change "<name>" --json` to see the next artifact and `openspec instructions <artifact-id> --change "<name>" --json` for how to create it)
|
||||
- If `state: "all_done"`: congratulate, suggest archive
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
Treat `context` as a required prompt-level input. Read and consider it, and
|
||||
apply relevant project facts, conventions, and constraints while implementing.
|
||||
Treat `operationGuidance` as optional additive advice. Read and consider every
|
||||
entry, and follow entries that are applicable and compatible with the built-in
|
||||
workflow.
|
||||
|
||||
Keep both fields separate from CLI-returned state, missing artifacts, tasks,
|
||||
progress, `contextFiles`, and the built-in `instruction`. They are not
|
||||
evidence of task completion, do not replace the built-in instruction, and do
|
||||
not permit bypassing a blocked state. If context conflicts with the built-in
|
||||
instruction, an explicit user choice, or a CLI-controlled value, report the
|
||||
conflict and preserve the controlling value. If guidance is inapplicable or
|
||||
conflicts with those controlling inputs, do not follow it and explain why.
|
||||
These are prompt-level behavior contracts, not enforceable checks.
|
||||
|
||||
4. **Read context files**
|
||||
|
||||
Read every file path listed under `contextFiles` from the apply instructions output.
|
||||
@@ -60,6 +77,9 @@ Implement tasks from an OpenSpec change.
|
||||
- **spec-driven**: proposal, specs, design, tasks
|
||||
- Other schemas: follow the contextFiles from CLI output
|
||||
|
||||
Do not copy `context` or `operationGuidance` verbatim into implementation
|
||||
files or planning artifacts unless the user separately asks for that content.
|
||||
|
||||
5. **Show current progress**
|
||||
|
||||
Display:
|
||||
@@ -119,7 +139,7 @@ Working on task 4/7: <task description>
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tasks complete! Ready to archive this change.
|
||||
All tasks complete! You can archive this change with `/opsx:archive`.
|
||||
```
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
@@ -151,6 +171,11 @@ What would you like to do?
|
||||
- Update task checkbox immediately after completing each task
|
||||
- Pause on errors, blockers, or unclear requirements - don't guess
|
||||
- Use contextFiles from CLI output, don't assume specific file names
|
||||
- Do not use context or operation guidance as proof that a task is complete
|
||||
- Apply relevant project context; report conflicts with controlling workflow inputs
|
||||
- Consider every guidance entry; explain any inapplicable or conflicting advice
|
||||
- Do not copy runtime context or operation guidance into implementation files or planning artifacts
|
||||
- Preserve CLI-controlled blocked/ready/all-done behavior and completion criteria
|
||||
|
||||
**Fluid Workflow Integration**
|
||||
|
||||
|
||||
@@ -7,25 +7,57 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve the full path from each delta spec when resolving its main spec.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
1. **Select the change**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
Show only active changes (not already archived).
|
||||
When prompting, show only active changes (not already archived).
|
||||
Include the schema used for each change if available.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:archive <other>`).
|
||||
|
||||
**Load current archive inputs before the existing archive checks:**
|
||||
|
||||
After resolving the selected change and planning root, run:
|
||||
```bash
|
||||
openspec instructions archive --change "<name>" --json
|
||||
```
|
||||
Keep the same selected-root flags on this command. This lookup is advisory and
|
||||
optional: it only supplies extra prompt inputs, so it must never block archiving.
|
||||
If it exits non-zero or returns invalid JSON — for example on an older CLI that
|
||||
does not support this command yet — continue the archive workflow with no
|
||||
context and no operation guidance. Do not report an error and do not stop.
|
||||
|
||||
A successful response may omit both optional fields. Treat `context` as a
|
||||
required prompt-level input: read and consider it, and apply relevant project
|
||||
facts, conventions, and constraints. Treat `operationGuidance` as optional
|
||||
additive advice: read and consider every entry, and follow entries that are
|
||||
applicable and compatible with the built-in archive workflow.
|
||||
|
||||
Keep both fields separate from built-in steps, explicit user choices, resolved
|
||||
paths, CLI checks, and command contracts. If context conflicts with one of those
|
||||
controlling inputs, report the conflict and preserve the controlling value. If
|
||||
guidance is inapplicable or conflicts with a controlling input, do not follow it
|
||||
and explain why. Do not infer replacement paths, skipped prompts, or flags from
|
||||
either field, and do not copy their text verbatim into specs, change artifacts,
|
||||
or archive summaries unless the user separately asks for it. These are
|
||||
prompt-level behavior contracts, not enforceable checks.
|
||||
|
||||
2. **Check artifact completion status**
|
||||
|
||||
@@ -34,11 +66,11 @@ Archive a completed change in the experimental workflow.
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context
|
||||
- `artifacts`: List of artifacts with their status (`done` or other)
|
||||
- `artifacts`: List of artifacts with their status (`done`, `skipped`, or other)
|
||||
|
||||
**If any artifacts are not `done`:**
|
||||
**If any artifacts are neither `done` nor `skipped`** (skipped artifacts satisfy the requirement - the change declares skip_specs):
|
||||
- Display warning listing incomplete artifacts
|
||||
- Use **AskUserQuestion tool** to confirm user wants to proceed
|
||||
- Ask the user to confirm they want to proceed
|
||||
- Proceed if user confirms
|
||||
|
||||
3. **Check task completion status**
|
||||
@@ -49,17 +81,20 @@ Archive a completed change in the experimental workflow.
|
||||
|
||||
**If incomplete tasks found:**
|
||||
- Display warning showing count of incomplete tasks
|
||||
- Use **AskUserQuestion tool** to confirm user wants to proceed
|
||||
- Ask the user to confirm they want to proceed
|
||||
- Proceed if user confirms
|
||||
|
||||
**If no tasks file exists:** Proceed without task-related warning.
|
||||
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.
|
||||
Use `artifactPaths.specs.existingOutputPaths` from status JSON as the only
|
||||
delta-spec source. If the `specs` entry is missing or
|
||||
`existingOutputPaths` is empty, proceed without a sync prompt and do not infer
|
||||
delta specs from other artifacts.
|
||||
|
||||
**If delta specs exist:**
|
||||
- Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`
|
||||
- Compare each delta spec with its corresponding main spec at `<planningHome.root>/openspec/specs/<capability-path>/spec.md` (use the store-aware `planningHome.root` from step 2, not a hardcoded repo path)
|
||||
- Determine what changes would be applied (adds, modifications, removals, renames)
|
||||
- Show a combined summary before prompting
|
||||
|
||||
@@ -67,7 +102,30 @@ Archive a completed change in the experimental workflow.
|
||||
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
||||
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
||||
|
||||
If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>"). Proceed to archive regardless of choice.
|
||||
Route on the answer:
|
||||
- "Cancel" — stop, do not archive
|
||||
- "Archive without syncing" or "Archive now" — proceed to archive
|
||||
- "Sync now" or "Sync anyway" — sync, then verify (below)
|
||||
- Anything else — ask again rather than archiving
|
||||
|
||||
Before a selected sync writes any main spec, run
|
||||
`openspec instructions specs --change "<name>" --json` once with the same
|
||||
selected-root flags. Require a zero exit status and valid artifact-instruction
|
||||
JSON. If the lookup fails or returns invalid JSON, report the error and stop
|
||||
before writing any main spec or moving the change. A valid response with omitted
|
||||
`rules` is the no-rules case. Apply returned `rules` only to the content and
|
||||
form of main specs produced by this merge; do not use them as archive guidance,
|
||||
change CLI behavior, or copy the rule text into any output file.
|
||||
|
||||
Then run the `openspec-sync-specs` workflow inline (agent-driven intelligent merge) for change '<name>', passing the delta spec analysis and the fetched specs-rule snapshot from above, and wait for it to finish. The inline sync must reuse that snapshot without fetching `specs` instructions again. Do not delegate it to a background task — step 5 would move `changeRoot` out from under a sync that is still reading it, leaving the change archived and the main specs never updated. If your agent can only run it by delegation, delegate synchronously and wait for the result.
|
||||
|
||||
Then re-run the comparison from the top of this step against every capability that has a delta spec in `artifactPaths.specs.existingOutputPaths` — not only the ones the sync reports it touched. A successful sync leaves nothing left to apply, so each capability must now read as already synced:
|
||||
- ADDED requirements present
|
||||
- MODIFIED requirements carrying the scenario and description changes named in the delta, with their other scenarios intact
|
||||
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving `## Requirements` empty), its main spec deleted rather than left empty; a spec the sync deliberately kept and reported is also a match
|
||||
- RENAMED requirements present under the new name and absent under the old one
|
||||
|
||||
If the sync failed, or any capability does not match, report what differs and stop — do not archive. Nothing has moved and `changeRoot` is intact, so the user can fix the mismatch or re-run the sync and start the archive again.
|
||||
|
||||
5. **Perform the archive**
|
||||
|
||||
@@ -76,14 +134,14 @@ Archive a completed change in the experimental workflow.
|
||||
mkdir -p "<planningHome.changesDir>/archive"
|
||||
```
|
||||
|
||||
Generate target name using current date: `YYYY-MM-DD-<change-name>`
|
||||
Generate the target name: use the change name as-is when it already starts with a `YYYY-MM-DD-` prefix; otherwise prepend the current date as `YYYY-MM-DD-<change-name>`. Never stack a second date (same rule as `openspec archive`).
|
||||
|
||||
**Check if target already exists:**
|
||||
- If yes: Fail with error, suggest renaming existing archive or using different date
|
||||
- If no: Move `changeRoot` to the archive directory
|
||||
|
||||
```bash
|
||||
mv "<changeRoot>" "<planningHome.changesDir>/archive/YYYY-MM-DD-<name>"
|
||||
mv "<changeRoot>" "<planningHome.changesDir>/archive/<target-name>"
|
||||
```
|
||||
|
||||
6. **Display summary**
|
||||
@@ -97,22 +155,28 @@ Archive a completed change in the experimental workflow.
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
```markdown
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/
|
||||
**Specs:** ✓ Synced to main specs (or "No delta specs" or "Sync skipped")
|
||||
**Archived to:** the archive path derived from `planningHome.changesDir`/<target-name>/
|
||||
**Specs:** <"✓ Synced to main specs" only if the step 4 verification passed; otherwise "No delta specs" or "Sync skipped">
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
<"All artifacts complete. All tasks complete." — or, if archived with warnings, list them instead (e.g. "Archived with 2 incomplete tasks")>
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Always prompt for change selection if not provided
|
||||
- Announce the selected change; prompt for selection when it is ambiguous
|
||||
- Use artifact graph (openspec status --json) for completion checking
|
||||
- Don't block archive on warnings - just inform and confirm
|
||||
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
||||
- Show clear summary of what happened
|
||||
- If sync is requested, use openspec-sync-specs approach (agent-driven)
|
||||
- If sync is requested, run the `openspec-sync-specs` workflow inline (agent-driven)
|
||||
- Never archive while a spec sync is still in flight — run the sync inline and verify the main specs before moving `changeRoot`
|
||||
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
||||
- Apply relevant runtime context and report conflicts; operation guidance remains advisory
|
||||
- Consider every guidance entry and explain any inapplicable or conflicting advice
|
||||
- Existing CLI checks, resolved paths, prompts, and command contracts are unchanged
|
||||
- Artifact rules constrain only the specs being written and are never operation guidance
|
||||
- Never copy runtime context, operation guidance, or artifact-rule text verbatim into output files
|
||||
|
||||
@@ -7,16 +7,16 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first and create a change proposal. You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing. For a new change, scaffold it first as described below.
|
||||
|
||||
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
---
|
||||
|
||||
@@ -94,6 +94,12 @@ This tells you:
|
||||
- Their names, schemas, and status
|
||||
- What the user might be working on
|
||||
|
||||
Then read the project's own context from the resolved root - `<root.path>/openspec/config.yaml` (or `config.yml`). Use the `root.path` returned above, and skip this if neither file exists:
|
||||
- `context`: project background - tech stack, conventions, constraints
|
||||
- `rules`: keyed by artifact id - the entries for an artifact apply only when you write that artifact
|
||||
|
||||
Ground your thinking in these. They are constraints for you to follow, not content to reproduce: do NOT copy them into the conversation or into any artifact you create.
|
||||
|
||||
### When no change exists
|
||||
|
||||
Think freely. When insights crystallize, you might offer:
|
||||
@@ -101,6 +107,15 @@ Think freely. When insights crystallize, you might offer:
|
||||
- "This feels solid enough to start a change. Want me to create a proposal?"
|
||||
- Or keep exploring - no pressure to formalize
|
||||
|
||||
If the user asks you to capture the exploration as a new change, transition seamlessly into the requested capture:
|
||||
|
||||
1. Run `openspec new change "<name>"` (with `--store <id>` when applicable) before creating any artifacts. Never create a new change directory under `openspec/changes/` by hand; the CLI scaffold creates required metadata such as `.openspec.yaml`. Keep the selected `--store <id>` on every applicable follow-up `status` and `instructions` command.
|
||||
2. Run `openspec status --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store), then process the requested artifacts in dependency order. For each requested artifact that is `ready`, run `openspec instructions "<artifact-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store). Before creating a requested artifact, evaluate any condition in its own `instruction` against the explored change; record a deliberate skip instead when the condition does not apply. If a requested artifact is blocked by a direct prerequisite the user did not request, run `openspec instructions "<prerequisite-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) for that prerequisite whether it is `ready` or `blocked`. If its own `instruction` states a condition, evaluate that condition against the explored change and record a deliberate skip only when the condition does not apply. If the condition applies, or the prerequisite is not conditional, treat it as a normal prerequisite and ask before expanding the capture. Do not create an unrequested prerequisite unless the user approves.
|
||||
3. Follow the returned `template` and `instruction` fields. Read completed dependency files listed in `dependencies`, and apply `context` and `rules` as constraints without copying them into the artifact. If the instruction delegates creation to a specific skill or command, invoke it; otherwise write the artifact to `resolvedOutputPath`, using the instruction to choose a concrete path when it is a glob. Verify that the selected concrete output exists.
|
||||
4. After creating each artifact, re-run `openspec status --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) and continue until every requested artifact is `done`, `skipped`, or was deliberately skipped because its own `instruction` stated a condition that did not apply. Tell the user about a deliberate conditional skip, remember it, and do not reconsider it. Dependencies are enablers, not gates: if a requested artifact is still `blocked` only because you deliberately skipped a conditional prerequisite, run `openspec instructions "<artifact-id>" --change "<name>" --json` (append the confirmed `--store "<id>"` only for a registered standalone store) despite the blocked status, then create it using step 3 only when those recorded conditional skips are its sole missing dependencies. If a requested artifact is blocked by a prerequisite the user did not ask to capture and cannot be conditionally skipped, explain that dependency and ask before expanding the capture.
|
||||
|
||||
Capture the artifact(s) the user requested without asking them to invoke another workflow command. If they asked only to start a change, stop after scaffolding and show its status.
|
||||
|
||||
### When a change exists
|
||||
|
||||
If the user mentions a change or you detect one is relevant:
|
||||
@@ -116,14 +131,16 @@ If the user mentions a change or you detect one is relevant:
|
||||
|
||||
3. **Offer to capture when decisions are made**
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|----------------------------|--------------------------------|
|
||||
| New requirement discovered | `specs/<capability>/spec.md` |
|
||||
| Requirement changed | `specs/<capability>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve an existing capability's full path and follow the project's established organization for new capabilities.
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|----------------------------|-------------------------------------|
|
||||
| New requirement discovered | `specs/<capability-path>/spec.md` |
|
||||
| Requirement changed | `specs/<capability-path>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
@@ -285,6 +302,7 @@ But this summary is optional. Sometimes the thinking IS the value.
|
||||
- **Don't rush** - Discovery is thinking time, not task time
|
||||
- **Don't force structure** - Let patterns emerge naturally
|
||||
- **Don't auto-capture** - Offer to save insights, don't just do it
|
||||
- **Don't manually scaffold changes** - Never create a new change directory under `openspec/changes/` by hand. Always use `openspec new change "<name>"` (with `--store <id>` when applicable) so required metadata such as `.openspec.yaml` is created before writing artifacts.
|
||||
- **Do visualize** - A good diagram is worth many paragraphs
|
||||
- **Do explore the codebase** - Ground discussions in reality
|
||||
- **Do question assumptions** - Including the user's and your own
|
||||
|
||||
@@ -1,281 +0,0 @@
|
||||
---
|
||||
name: openspec-lock-consensus
|
||||
description: 锁定共识 - 在探索讨论后,将讨论结果锁定为正式共识文档。防止后续提案偏离讨论内容。
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: junhong
|
||||
version: "1.1"
|
||||
---
|
||||
|
||||
# 共识锁定 Skill
|
||||
|
||||
在 `/opsx:explore` 讨论后,使用此 skill 将讨论结果锁定为正式共识。共识文档是后续所有 artifact 的基础约束。
|
||||
|
||||
## 触发方式
|
||||
|
||||
```
|
||||
/opsx:lock <change-name>
|
||||
```
|
||||
|
||||
或在探索结束后,AI 主动提议:
|
||||
> "讨论已经比较清晰了,要锁定共识吗?"
|
||||
|
||||
---
|
||||
|
||||
## 工作流程
|
||||
|
||||
### Step 1: 整理讨论要点
|
||||
|
||||
从对话中提取以下四个维度的共识:
|
||||
|
||||
| 维度 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| **要做什么** | 明确的功能范围 | "支持批量导入 IoT 卡" |
|
||||
| **不做什么** | 明确排除的内容 | "不支持实时同步,仅定时批量" |
|
||||
| **关键约束** | 技术/业务限制 | "必须使用 Asynq 异步任务" |
|
||||
| **验收标准** | 如何判断完成 | "导入 1000 张卡 < 30s" |
|
||||
|
||||
### Step 2: 使用 Question_tool 逐维度确认
|
||||
|
||||
**必须使用 Question_tool 进行结构化确认**,每个维度一个问题:
|
||||
|
||||
```typescript
|
||||
// 示例:确认"要做什么"
|
||||
Question_tool({
|
||||
questions: [{
|
||||
header: "确认:要做什么",
|
||||
question: "以下是整理的功能范围,请确认:\n\n" +
|
||||
"1. 功能点 A\n" +
|
||||
"2. 功能点 B\n" +
|
||||
"3. 功能点 C\n\n" +
|
||||
"是否准确完整?",
|
||||
options: [
|
||||
{ label: "确认无误", description: "以上内容准确完整" },
|
||||
{ label: "需要补充", description: "有遗漏的功能点" },
|
||||
{ label: "需要删减", description: "有不应该包含的内容" }
|
||||
],
|
||||
multiple: false
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
**如果用户选择"需要补充"或"需要删减"**:
|
||||
- 用户会通过自定义输入提供修改意见
|
||||
- 根据反馈更新列表,再次使用 Question_tool 确认
|
||||
|
||||
**确认流程**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Question_tool: 确认"要做什么" │
|
||||
│ ├── 用户选择"确认无误" → 进入下一维度 │
|
||||
│ └── 用户选择其他/自定义 → 修改后重新确认 │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ Question_tool: 确认"不做什么" │
|
||||
│ ├── 用户选择"确认无误" → 进入下一维度 │
|
||||
│ └── 用户选择其他/自定义 → 修改后重新确认 │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ Question_tool: 确认"关键约束" │
|
||||
│ ├── 用户选择"确认无误" → 进入下一维度 │
|
||||
│ └── 用户选择其他/自定义 → 修改后重新确认 │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ Question_tool: 确认"验收标准" │
|
||||
│ ├── 用户选择"确认无误" → 生成 consensus.md │
|
||||
│ └── 用户选择其他/自定义 → 修改后重新确认 │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Step 3: 生成 consensus.md
|
||||
|
||||
所有维度确认后,创建文件:
|
||||
|
||||
```bash
|
||||
# 检查 change 是否存在
|
||||
openspec list --json
|
||||
|
||||
# 如果 change 不存在,先创建
|
||||
# openspec new <change-name>
|
||||
|
||||
# 写入 consensus.md
|
||||
```
|
||||
|
||||
**文件路径**: `openspec/changes/<change-name>/consensus.md`
|
||||
|
||||
---
|
||||
|
||||
## Question_tool 使用规范
|
||||
|
||||
### 每个维度的问题模板
|
||||
|
||||
**1. 要做什么**
|
||||
```typescript
|
||||
{
|
||||
header: "确认:要做什么",
|
||||
question: "以下是整理的【功能范围】:\n\n" +
|
||||
items.map((item, i) => `${i+1}. ${item}`).join('\n') +
|
||||
"\n\n请确认是否准确完整?",
|
||||
options: [
|
||||
{ label: "确认无误", description: "功能范围准确完整" },
|
||||
{ label: "需要补充", description: "有遗漏的功能点" },
|
||||
{ label: "需要删减", description: "有不应该包含的内容" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**2. 不做什么**
|
||||
```typescript
|
||||
{
|
||||
header: "确认:不做什么",
|
||||
question: "以下是明确【排除的内容】:\n\n" +
|
||||
items.map((item, i) => `${i+1}. ${item}`).join('\n') +
|
||||
"\n\n请确认是否正确?",
|
||||
options: [
|
||||
{ label: "确认无误", description: "排除范围正确" },
|
||||
{ label: "需要补充", description: "还有其他需要排除的" },
|
||||
{ label: "需要删减", description: "有些不应该排除" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**3. 关键约束**
|
||||
```typescript
|
||||
{
|
||||
header: "确认:关键约束",
|
||||
question: "以下是【关键约束】:\n\n" +
|
||||
items.map((item, i) => `${i+1}. ${item}`).join('\n') +
|
||||
"\n\n请确认是否正确?",
|
||||
options: [
|
||||
{ label: "确认无误", description: "约束条件正确" },
|
||||
{ label: "需要补充", description: "还有其他约束" },
|
||||
{ label: "需要修改", description: "约束描述不准确" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**4. 验收标准**
|
||||
```typescript
|
||||
{
|
||||
header: "确认:验收标准",
|
||||
question: "以下是【验收标准】(必须可测量):\n\n" +
|
||||
items.map((item, i) => `${i+1}. ${item}`).join('\n') +
|
||||
"\n\n请确认是否正确?",
|
||||
options: [
|
||||
{ label: "确认无误", description: "验收标准清晰可测量" },
|
||||
{ label: "需要补充", description: "还有其他验收标准" },
|
||||
{ label: "需要修改", description: "标准不够清晰或无法测量" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 处理用户反馈
|
||||
|
||||
当用户选择非"确认无误"选项或提供自定义输入时:
|
||||
|
||||
1. 解析用户的修改意见
|
||||
2. 更新对应维度的内容
|
||||
3. 再次使用 Question_tool 确认更新后的内容
|
||||
4. 重复直到用户选择"确认无误"
|
||||
|
||||
---
|
||||
|
||||
## consensus.md 模板
|
||||
|
||||
```markdown
|
||||
# 共识文档
|
||||
|
||||
**Change**: <change-name>
|
||||
**确认时间**: <timestamp>
|
||||
**确认人**: 用户
|
||||
|
||||
---
|
||||
|
||||
## 1. 要做什么
|
||||
|
||||
- [x] 功能点 A(已确认)
|
||||
- [x] 功能点 B(已确认)
|
||||
- [x] 功能点 C(已确认)
|
||||
|
||||
## 2. 不做什么
|
||||
|
||||
- [x] 排除项 A(已确认)
|
||||
- [x] 排除项 B(已确认)
|
||||
|
||||
## 3. 关键约束
|
||||
|
||||
- [x] 技术约束 A(已确认)
|
||||
- [x] 业务约束 B(已确认)
|
||||
|
||||
## 4. 验收标准
|
||||
|
||||
- [x] 验收标准 A(已确认)
|
||||
- [x] 验收标准 B(已确认)
|
||||
|
||||
---
|
||||
|
||||
## 讨论背景
|
||||
|
||||
<简要总结讨论的核心问题和解决方向>
|
||||
|
||||
## 关键决策记录
|
||||
|
||||
| 决策点 | 选择 | 原因 |
|
||||
|--------|------|------|
|
||||
| 决策 1 | 选项 A | 理由... |
|
||||
| 决策 2 | 选项 B | 理由... |
|
||||
|
||||
---
|
||||
|
||||
**签字确认**: 用户已通过 Question_tool 逐条确认以上内容
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 后续流程绑定
|
||||
|
||||
### Proposal 生成时
|
||||
|
||||
`/opsx:continue` 生成 proposal 时,**必须**:
|
||||
|
||||
1. 读取 `consensus.md`
|
||||
2. 确保 proposal 的 Capabilities 覆盖"要做什么"中的每一项
|
||||
3. 确保 proposal 不包含"不做什么"中的内容
|
||||
4. 确保 proposal 遵守"关键约束"
|
||||
|
||||
### 验证机制
|
||||
|
||||
如果 proposal 与 consensus 不一致,输出警告:
|
||||
|
||||
```
|
||||
⚠️ Proposal 验证警告:
|
||||
|
||||
共识中"要做什么"但 Proposal 未提及:
|
||||
- 功能点 C
|
||||
|
||||
共识中"不做什么"但 Proposal 包含:
|
||||
- 排除项 A
|
||||
|
||||
建议修正 Proposal 或更新共识。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **必须使用 Question_tool** - 不要用纯文本确认
|
||||
- **逐维度确认** - 四个维度分开确认,不要合并
|
||||
- **不要跳过确认** - 每个维度都必须让用户明确确认
|
||||
- **不要自作主张** - 只整理讨论中明确提到的内容
|
||||
- **避免模糊表述** - "尽量"、"可能"、"考虑"等词汇需要明确化
|
||||
- **验收标准必须可测量** - 避免"性能要好"这类无法验证的标准
|
||||
|
||||
---
|
||||
|
||||
## 与其他 Skills 的关系
|
||||
|
||||
| Skill | 关系 |
|
||||
|-------|------|
|
||||
| `openspec-explore` | 探索结束后触发 lock |
|
||||
| `openspec-new-change` | lock 后触发 new(如果 change 不存在)|
|
||||
| `openspec-continue-change` | 生成 proposal 时读取 consensus 验证 |
|
||||
| `openspec-generate-acceptance-tests` | 从 consensus 的验收标准生成测试骨架 |
|
||||
@@ -7,53 +7,79 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Propose a new change - create the change and generate all artifacts in one step.
|
||||
|
||||
I'll create a change with artifacts:
|
||||
**Planning boundary**: This workflow creates planning artifacts only. The user request that selected or triggered this workflow authorizes planning only, even if it asks to build or fix something. Do not edit project code. After the planning artifacts are complete, stop. Do not start implementation in the same response, even if the initial request asks for it. Wait for a new user request after the artifacts are presented; then start the apply workflow.
|
||||
|
||||
I'll create a change with the artifacts your schema defines. With the default spec-driven schema that is:
|
||||
- proposal.md (what & why)
|
||||
- `specs/<capability-path>/spec.md` (what the system must do - a delta, not the main spec)
|
||||
- design.md (how)
|
||||
- tasks.md (implementation steps)
|
||||
|
||||
When ready to implement, run /opsx:apply
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve an existing capability's full path and follow the project's established organization for new capabilities.
|
||||
|
||||
When the user is ready to implement, they must start the apply workflow explicitly.
|
||||
|
||||
---
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no clear input provided, ask what they want to build**
|
||||
1. **Understand the request and clarify material ambiguity**
|
||||
|
||||
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
||||
If no clear input is provided, ask the user (open-ended, no preset options):
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
2. **Create the change directory**
|
||||
If the request contains ambiguity that would materially affect scope, externally observable behavior, compatibility, or acceptance criteria, ask the user before creating the change. For minor details, make a reasonable assumption and record it in the planning artifacts.
|
||||
|
||||
2. **Determine the workflow schema**
|
||||
|
||||
Use the configured default schema unless the user explicitly requests a different workflow.
|
||||
|
||||
**Use a different schema only if the user:**
|
||||
- Explicitly requests a specific schema by name → use `--schema <schema-name>`
|
||||
- Asks to "show workflows" or asks "what workflows" exist → resolve the authoritative root by running `openspec context --json` from the current working directory. If the user explicitly selected a registered store, use `openspec context --json --store "<store-id>"`. Then run `openspec schemas --json` with its working directory set to the returned `root.path` and let them choose. This preserves roots selected by a local `store:` pointer or the global `defaultStore`; `schemas` does not accept `--store`. If context reports only `no_openspec_root`, run `openspec schemas --json` from the current working directory instead. Do not use this fallback for invalid or unavailable stores.
|
||||
|
||||
Otherwise, omit `--schema` to preserve the configured default.
|
||||
|
||||
3. **Create the change directory**
|
||||
|
||||
Choose one schema form below. If a registered store is selected, append `--store "<store-id>"` to that command and each later OpenSpec command shown below that accepts `--store`.
|
||||
|
||||
Using the configured default:
|
||||
```bash
|
||||
openspec new change "<name>"
|
||||
```
|
||||
|
||||
Using an explicitly requested schema:
|
||||
```bash
|
||||
openspec new change "<name>" --schema "<schema-name>"
|
||||
```
|
||||
This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.
|
||||
|
||||
3. **Get the artifact build order**
|
||||
4. **Get the artifact build order**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to get:
|
||||
- `applyRequires`: array of artifact IDs needed before implementation (e.g., `["tasks"]`)
|
||||
- `artifacts`: list of all artifacts with their status and dependencies
|
||||
- `artifacts`: list of all artifacts, each with its `status` and its `requires` edges (the artifact IDs it directly depends on)
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.
|
||||
|
||||
4. **Create artifacts in sequence until apply-ready**
|
||||
5. **Create every artifact in the required set**
|
||||
|
||||
Use the **TodoWrite tool** to track progress through the artifacts.
|
||||
Use a todo list to track progress through the artifacts.
|
||||
|
||||
Loop through artifacts in dependency order (artifacts with no pending dependencies first):
|
||||
|
||||
@@ -67,23 +93,30 @@ When ready to implement, run /opsx:apply
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance for this artifact type
|
||||
- `skipped`/`warning`: present when the change declares skip_specs and this artifact must NOT be created - stop and pick another artifact
|
||||
- `resolvedOutputPath`: Resolved path or pattern to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- Read any completed dependency files for context
|
||||
- Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`
|
||||
- Read any completed dependency files for context - always re-read them from disk, even if you saw them earlier in the conversation (the user may have edited them)
|
||||
- If the `instruction` field delegates creation to a specific skill or command, invoke it to produce the artifact instead of writing the file yourself, then verify the artifact file exists at `resolvedOutputPath`
|
||||
- Otherwise create the artifact file using `template` as the structure and write it to `resolvedOutputPath`. If `resolvedOutputPath` is a glob, follow `instruction` to choose the concrete file path
|
||||
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
||||
- Show brief progress: "Created <artifact-id>"
|
||||
|
||||
b. **Continue until all `applyRequires` artifacts are complete**
|
||||
b. **Continue until every artifact in the required set exists (not just `apply.requires`)**
|
||||
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
||||
- Check if every artifact ID in `applyRequires` has `status: "done"` in the artifacts array
|
||||
- Stop when all `applyRequires` artifacts are done
|
||||
- The required set is `applyRequires` plus every artifact reachable from those by following the `requires` edges in `status --json` - walk them transitively (spec-driven closes over proposal, specs, design, tasks). Leave artifacts outside that set alone
|
||||
- `status` is file-existence only, so an `applyRequires` artifact reading `done` does NOT mean its dependencies exist - writing `tasks.md` early marks `tasks` done while `specs` was never written. Use each artifact's `requires` edges, not its `status`, to build the required set: a `done` artifact still lists what it depends on
|
||||
- An artifact already reading `status: "skipped"` is satisfied: the change declares `skip_specs` in `.openspec.yaml`, so its files must NOT exist. Never try to create one
|
||||
- Create every artifact in the required set that is missing, then re-check - creating one can unblock others
|
||||
- Skip one only when `status` already reports it `skipped`, or when its own `instruction` says it is conditional: run `openspec instructions <artifact-id> --change "<name>" --json` and skip only if its `instruction` field marks it optional (e.g. "create only if..."). Spec-driven's `design.md` qualifies; `specs` qualifies only via the `skipped` status above, never by your own judgment. Tell the user, and do not reconsider it
|
||||
- Dependencies are enablers, not gates: if a required artifact is still `blocked` only because you skipped a conditional dependency, write it anyway
|
||||
- Stop when every artifact in the required set is `done`, `skipped`, or was deliberately skipped
|
||||
|
||||
c. **If an artifact requires user input** (unclear context):
|
||||
- Use **AskUserQuestion tool** to clarify
|
||||
- Ask the user to clarify
|
||||
- Then continue with creation
|
||||
|
||||
5. **Show final status**
|
||||
6. **Show final status**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
@@ -92,13 +125,14 @@ When ready to implement, run /opsx:apply
|
||||
|
||||
After completing all artifacts, summarize:
|
||||
- Change name and location
|
||||
- List of artifacts created with brief descriptions
|
||||
- What's ready: "All artifacts created! Ready for implementation."
|
||||
- Prompt: "Run `/opsx:apply` or ask me to implement to start working on the tasks."
|
||||
- List of artifacts created with brief descriptions, plus any conditional artifact you skipped and why
|
||||
- What's ready: "All artifacts needed for implementation are ready."
|
||||
- Prompt: "The artifacts are ready for review. When you are ready, run `/opsx:apply` or ask me to apply this change."
|
||||
|
||||
**Artifact Creation Guidelines**
|
||||
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type - it is the authoritative guidance, even for familiar artifact names
|
||||
- If the `instruction` field directs you to use a specific skill or command to create the artifact, invoke it instead of writing the artifact directly
|
||||
- The schema defines what each artifact should contain - follow it
|
||||
- Read dependency artifacts for context before creating new ones
|
||||
- Use `template` as the structure for your output file - fill in its sections
|
||||
@@ -107,8 +141,9 @@ After completing all artifacts, summarize:
|
||||
- These guide what you write, but should never appear in the output
|
||||
|
||||
**Guardrails**
|
||||
- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)
|
||||
- Always read dependency artifacts before creating a new one
|
||||
- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum
|
||||
- The request that invoked this workflow authorizes planning only. Any implementation or apply instruction in that request does not carry forward. Do NOT implement the change, start the apply workflow, or edit project code during this workflow. After presenting the artifacts, stop and wait for a new user request to start the apply workflow
|
||||
- Create every artifact the apply phase transitively depends on, not just the ids listed in `apply.requires`
|
||||
- Always read dependency artifacts before creating a new one - re-read from disk, not from conversation memory (files may have changed since you last saw them)
|
||||
- Ask about ambiguities that would materially change scope, externally observable behavior, compatibility, or acceptance criteria; for minor details, make reasonable assumptions and record them
|
||||
- If a change with that name already exists, ask if user wants to continue it or create a new one
|
||||
- Verify each artifact file exists after writing before proceeding to next
|
||||
|
||||
@@ -7,26 +7,31 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Sync delta specs from a change to main specs.
|
||||
|
||||
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
`<capability-path>` is the spec directory relative to `specs/` (for example, `user-auth` or `identity/user-auth`). Preserve the full path from each delta spec when resolving its main spec.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
1. **Select the change**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
Show changes that have delta specs (under `specs/` directory).
|
||||
When prompting, show changes that have delta specs (under `specs/` directory).
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:sync <other>`).
|
||||
|
||||
2. **Resolve change context**
|
||||
|
||||
@@ -35,9 +40,29 @@ This is an **agent-driven** operation - you will read delta specs and directly e
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
|
||||
The JSON includes `planningHome.root`. Main specs live under `<planningHome.root>/openspec/specs/` — use that (store-aware) root for every main-spec path below, not a hardcoded repo path. When a store is selected it points at the store, not the current repository.
|
||||
|
||||
3. **Find delta specs**
|
||||
|
||||
Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.
|
||||
Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the
|
||||
only source of delta spec paths. If the `specs` entry is missing or
|
||||
`existingOutputPaths` is empty, report that there are no delta specs to sync,
|
||||
do not infer them from other artifacts, and stop without requesting artifact
|
||||
instructions or writing a main spec.
|
||||
|
||||
Sync every path in `existingOutputPaths` unless the caller narrowed the set.
|
||||
A caller narrows it by naming an explicit list of complete entries from
|
||||
`existingOutputPaths` — copy those absolute values verbatim. Archive does
|
||||
this inline, and a user can too (for example, by selecting the entry ending
|
||||
in `/specs/billing/invoices/spec.md`).
|
||||
Then sync only the named paths and leave the remaining delta specs untouched:
|
||||
bulk archive excludes a delta whose implementation it could not find, and
|
||||
syncing it anyway would write a main spec the caller deliberately withheld.
|
||||
Carry that narrowed selection through step 4; never widen it back to the full
|
||||
list. If a named path is not in `existingOutputPaths`, do not sync it —
|
||||
report it and stop, rather than dropping it silently. If the named list is
|
||||
empty, report that there is nothing to sync and stop without writing a main
|
||||
spec.
|
||||
|
||||
Each delta spec file contains sections like:
|
||||
- `## ADDED Requirements` - New requirements to add
|
||||
@@ -49,11 +74,27 @@ This is an **agent-driven** operation - you will read delta specs and directly e
|
||||
|
||||
4. **For each delta spec, apply changes to main specs**
|
||||
|
||||
For each repo-local capability delta spec path returned by the CLI:
|
||||
Before the first main-spec write, obtain one current specs-rule snapshot:
|
||||
- If archive invoked this workflow inline and supplied a valid snapshot from
|
||||
`openspec instructions specs --change "<name>" --json`, reuse it and do not
|
||||
fetch the same instructions again.
|
||||
- Otherwise run that command once now with the same selected-root flags.
|
||||
- If the direct lookup exits non-zero or returns invalid artifact-instruction
|
||||
JSON, report the error and stop before writing any main spec. Do not treat the
|
||||
failure as an absent rule set.
|
||||
- A valid response with omitted `rules` means no artifact rules are configured
|
||||
and the existing semantic merge continues.
|
||||
|
||||
Apply returned `rules` only to the content and form of the main specs produced
|
||||
by this merge. Artifact rules are not operation guidance and cannot change
|
||||
selected roots, delta paths, CLI checks, or workflow steps. Use their text as
|
||||
constraints without copying it verbatim into a main spec or summary.
|
||||
|
||||
For each capability delta spec path selected in step 3 — the full `existingOutputPaths` list, or the narrowed subset when a caller supplied one (these may belong to a selected store, not the repo):
|
||||
|
||||
a. **Read the delta spec** to understand the intended changes
|
||||
|
||||
b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)
|
||||
b. **Read the main spec** at `<planningHome.root>/openspec/specs/<capability-path>/spec.md` (may not exist yet)
|
||||
|
||||
c. **Apply changes intelligently**:
|
||||
|
||||
@@ -64,31 +105,72 @@ This is an **agent-driven** operation - you will read delta specs and directly e
|
||||
**MODIFIED Requirements:**
|
||||
- Find the requirement in main spec
|
||||
- Apply the changes - this can be:
|
||||
- Adding new scenarios (don't need to copy existing ones)
|
||||
- Adding new scenarios the main spec does not have yet
|
||||
- Modifying existing scenarios
|
||||
- Changing the requirement description
|
||||
- Preserve scenarios/content not mentioned in the delta
|
||||
|
||||
**REMOVED Requirements:**
|
||||
- Remove the entire requirement block from main spec
|
||||
- Retiring the capability. Delete the whole `spec.md` - and the directory once
|
||||
nothing else is left in it - only when ALL of these hold:
|
||||
1. removing the requirements *this run* left no requirement blocks;
|
||||
2. the rest of the spec is well-formed (it still has a `## Purpose`);
|
||||
3. the main spec was not already empty before this sync - if you removed
|
||||
nothing, change nothing;
|
||||
4. every other nonblank line in the whole file is accounted for as the
|
||||
title, Purpose, Requirements header, or a canonical requirement's
|
||||
statement, scenarios, or fenced examples;
|
||||
5. the change's `.openspec.yaml` declares `retire_capabilities: true`;
|
||||
6. the `spec.md` resolves inside the real specs root (do not follow a
|
||||
capability-directory symlink to delete an external file).
|
||||
If removing the selected requirements would leave no requirement blocks and
|
||||
any retirement condition is not satisfied, do not modify the main spec. Stop
|
||||
the sync for that capability, report the blocking condition, and tell the user
|
||||
how to resolve it. Never write or leave an empty `## Requirements` section.
|
||||
When only the marker is missing, say that too - it is the one thing the user
|
||||
can add to make the retirement go through.
|
||||
- Deleting the file also deletes its `## Purpose`; any other section blocks
|
||||
retirement. Name Purpose when you report the retirement. Include a pasteable
|
||||
`git checkout` only when the spec lived in the caller's checkout;
|
||||
otherwise give checkout-scoped recovery guidance.
|
||||
|
||||
**RENAMED Requirements:**
|
||||
- Find the FROM requirement, rename to TO
|
||||
|
||||
d. **Create new main spec** if capability doesn't exist yet:
|
||||
- Create `openspec/specs/<capability>/spec.md`
|
||||
- Add Purpose section (can be brief, mark as TBD)
|
||||
- Add Requirements section with the ADDED requirements
|
||||
**`## Purpose` in the delta:**
|
||||
- The main spec already has one and it is authoritative - leave it alone
|
||||
(this is what `openspec archive` does; it warns and moves on)
|
||||
|
||||
5. **Show summary**
|
||||
d. **Create new main spec** if capability doesn't exist yet:
|
||||
- Create `<planningHome.root>/openspec/specs/<capability-path>/spec.md`
|
||||
- Add Purpose section: copy the delta's `## Purpose` body verbatim when it has one
|
||||
(this is what `openspec archive` does); only write a brief TBD placeholder when it does not
|
||||
- Add Requirements section with the ADDED requirements
|
||||
- Follow the **Main Spec Format Reference** below
|
||||
|
||||
5. **Validate updated main specs**
|
||||
|
||||
Run `openspec validate --specs` with the same selected-root flags used earlier.
|
||||
If validation fails, report the problems and do not claim the sync succeeded.
|
||||
|
||||
6. **Show summary**
|
||||
|
||||
After applying all changes, summarize:
|
||||
- Which capabilities were updated
|
||||
- What changes were made (requirements added/modified/removed/renamed)
|
||||
- Any new main spec left with a TBD Purpose placeholder, so it gets written
|
||||
now rather than lingering
|
||||
- Any capability retired, naming the deleted `spec.md`, its Purpose, and
|
||||
either a pasteable `git checkout` or checkout-scoped recovery guidance
|
||||
|
||||
**Delta Spec Format Reference**
|
||||
|
||||
```markdown
|
||||
## Purpose
|
||||
|
||||
Only on a delta that introduces a brand-new capability. Seeds the new main spec.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: New Feature
|
||||
@@ -101,6 +183,12 @@ The system SHALL do something new.
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Existing Feature
|
||||
The system SHALL keep doing the existing thing, now also handling A.
|
||||
|
||||
#### Scenario: Scenario the main spec already has
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
|
||||
#### Scenario: New scenario to add
|
||||
- **WHEN** user does A
|
||||
- **THEN** system does B
|
||||
@@ -115,16 +203,36 @@ The system SHALL do something new.
|
||||
- TO: `### Requirement: New Name`
|
||||
```
|
||||
|
||||
**Main Spec Format Reference**
|
||||
|
||||
Main specs are what the delta merges INTO. They must never contain delta operation headers (`## ADDED/MODIFIED/REMOVED/RENAMED Requirements`) - after syncing, every requirement lives under a single `## Requirements` section:
|
||||
|
||||
```markdown
|
||||
# <capability> Specification
|
||||
|
||||
## Purpose
|
||||
Short description of what this capability does and why it exists.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: New Feature
|
||||
The system SHALL do something new.
|
||||
|
||||
#### Scenario: Basic case
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
```
|
||||
|
||||
**Key Principle: Intelligent Merging**
|
||||
|
||||
Unlike programmatic merging, you can apply **partial updates**:
|
||||
- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios
|
||||
- The delta represents *intent*, not a wholesale replacement
|
||||
Unlike programmatic merging, you merge rather than overwrite:
|
||||
- A MODIFIED block carries the whole requirement - body plus every scenario that survives the change. `openspec validate` and `openspec archive` both reject one that drops a scenario the main spec still has.
|
||||
- Keep anything the delta does not mention, in the main spec's existing order
|
||||
- Use your judgment to merge changes sensibly
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
```markdown
|
||||
## Specs Synced: <change-name>
|
||||
|
||||
Updated main specs:
|
||||
@@ -143,6 +251,12 @@ Main specs are now updated. The change remains active - archive when implementat
|
||||
**Guardrails**
|
||||
- Read both delta and main specs before making changes
|
||||
- Preserve existing content not mentioned in delta
|
||||
- Never copy a delta file into a main spec as-is - merge its content so the main spec keeps the Main Spec Format Reference structure, with no delta operation headers
|
||||
- If something is unclear, ask for clarification
|
||||
- Show what you're changing as you go
|
||||
- The operation should be idempotent - running twice should give same result
|
||||
- Use only `artifactPaths.specs.existingOutputPaths`; never infer delta specs from unrelated artifacts
|
||||
- Honor a caller-supplied subset of `existingOutputPaths`; never widen it back to the full list
|
||||
- Fetch specs instructions once for direct sync, or reuse the archive-supplied snapshot inline
|
||||
- Stop before every main-spec write on a non-zero or invalid JSON specs-instruction response
|
||||
- Artifact rules constrain only the specs being written and are never copied into output files
|
||||
|
||||
@@ -7,22 +7,27 @@ compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
generatedBy: "1.8.0"
|
||||
---
|
||||
|
||||
Revise a change's existing planning artifacts and keep them coherent. Never edit code.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`, `view`). Once selected, treat `--store <id>` as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run `openspec status --change "<name>" --json --store "<id>"`, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
`/opsx:continue` is an expanded-profile workflow and may not be installed. Before suggesting it anywhere below, verify that it is available. If it is unavailable, `openspec status --change "<name>" --json` shows the next artifact and `openspec instructions "<artifact-id>" --change "<name>" --json` explains how to create it.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
1. **Select the change**
|
||||
|
||||
Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to update.
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes sorted by most recently modified, and ask the user to select one
|
||||
|
||||
Present the top 3-4 most recently modified changes as options, showing:
|
||||
When prompting, present the top 3-4 most recently modified changes as options, showing:
|
||||
- Change name
|
||||
- Schema (from `schema` field if present, otherwise "spec-driven")
|
||||
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
||||
@@ -30,7 +35,7 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
|
||||
|
||||
Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to update.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:update <other>`).
|
||||
|
||||
2. **Get the change's artifacts**
|
||||
```bash
|
||||
@@ -38,8 +43,8 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
|
||||
```
|
||||
Parse the JSON to understand current state. The response includes:
|
||||
- `schemaName`: The workflow schema being used (e.g., "spec-driven")
|
||||
- `artifacts`: Array of artifacts with their status ("done", "ready", "blocked")
|
||||
- `isComplete`: Boolean indicating if all artifacts are complete
|
||||
- `artifacts`: Array of artifacts with their status ("done", "skipped", "ready", "blocked")
|
||||
- `isPlanningComplete`: Boolean indicating if all planning artifacts are complete. Older CLI versions expose the same value as `isComplete`.
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.
|
||||
|
||||
The artifact ids and paths come from the active schema - do NOT assume them, and do NOT branch on hardcoded artifact names. Custom schemas must work unchanged.
|
||||
@@ -62,7 +67,7 @@ Revise a change's existing planning artifacts and keep them coherent. Never edit
|
||||
- If the user rejects a revision, do not write it - leave that artifact unchanged.
|
||||
- When a substantial rewrite is needed, get that artifact's rules and template first:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
openspec instructions "<artifact-id>" --change "<name>" --json
|
||||
```
|
||||
|
||||
6. **Point to the next step (guidance only - NEVER act on it)**
|
||||
@@ -83,4 +88,4 @@ After each invocation, show:
|
||||
- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.
|
||||
- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/opsx:continue`'s job.
|
||||
- Confirm every edit with the user before writing.
|
||||
- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/opsx:new` (the "Update vs. Start Fresh" heuristic).
|
||||
- If the request changes the change's *intent* rather than refining it, first verify whether the expanded-profile `/opsx:new` workflow is available. If it is, recommend starting fresh with `/opsx:new` (the "Update vs. Start Fresh" heuristic). If it is unavailable, ask for a distinct unused change name and recommend `openspec new change "<new-change-name>"` instead.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../.agents/skills/to-issues
|
||||
@@ -1 +0,0 @@
|
||||
../../.agents/skills/to-prd
|
||||
1
.claude/skills/to-questionnaire
Symbolic link
1
.claude/skills/to-questionnaire
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/to-questionnaire
|
||||
1
.claude/skills/wait-what
Symbolic link
1
.claude/skills/wait-what
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/wait-what
|
||||
1
.claude/skills/wizard
Symbolic link
1
.claude/skills/wizard
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/wizard
|
||||
@@ -1 +0,0 @@
|
||||
../../.agents/skills/write-a-skill
|
||||
1
.claude/skills/writing-for-agents
Symbolic link
1
.claude/skills/writing-for-agents
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.agents/skills/writing-for-agents
|
||||
@@ -1 +0,0 @@
|
||||
../../.agents/skills/writing-great-skills
|
||||
@@ -1 +0,0 @@
|
||||
../../.agents/skills/zoom-out
|
||||
@@ -1,150 +0,0 @@
|
||||
---
|
||||
description: Implement tasks from an OpenSpec change (Experimental)
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Implement tasks from an OpenSpec change.
|
||||
|
||||
**Input**: Optionally specify a change name (e.g., `/opsx:apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Select the change**
|
||||
|
||||
If a name is provided, use it. Otherwise:
|
||||
- Infer from conversation context if the user mentioned a change
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
||||
|
||||
Always announce: "Using change: <name>" and how to override (e.g., `/opsx:apply <other>`).
|
||||
|
||||
2. **Check status to understand the schema**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
||||
- Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
|
||||
|
||||
3. **Get apply instructions**
|
||||
|
||||
```bash
|
||||
openspec instructions apply --change "<name>" --json
|
||||
```
|
||||
|
||||
This returns:
|
||||
- Context file paths (varies by schema)
|
||||
- Progress (total, complete, remaining)
|
||||
- Task list with status
|
||||
- Dynamic instruction based on current state
|
||||
|
||||
**Handle states:**
|
||||
- If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx:continue`
|
||||
- If `state: "all_done"`: congratulate, suggest archive
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
4. **Read context files**
|
||||
|
||||
Read the files listed in `contextFiles` from the apply instructions output.
|
||||
The files depend on the schema being used:
|
||||
- **spec-driven**: proposal, specs, design, tasks
|
||||
- Other schemas: follow the contextFiles from CLI output
|
||||
|
||||
5. **Show current progress**
|
||||
|
||||
Display:
|
||||
- Schema being used
|
||||
- Progress: "N/M tasks complete"
|
||||
- Remaining tasks overview
|
||||
- Dynamic instruction from CLI
|
||||
|
||||
6. **Implement tasks (loop until done or blocked)**
|
||||
|
||||
For each pending task:
|
||||
- Show which task is being worked on
|
||||
- Make the code changes required
|
||||
- Keep changes minimal and focused
|
||||
- Mark task complete in the tasks file: `- [ ]` → `- [x]`
|
||||
- Continue to next task
|
||||
|
||||
**Pause if:**
|
||||
- Task is unclear → ask for clarification
|
||||
- Implementation reveals a design issue → suggest updating artifacts
|
||||
- Error or blocker encountered → report and wait for guidance
|
||||
- User interrupts
|
||||
|
||||
7. **On completion or pause, show status**
|
||||
|
||||
Display:
|
||||
- Tasks completed this session
|
||||
- Overall progress: "N/M tasks complete"
|
||||
- If all done: suggest archive
|
||||
- If paused: explain why and wait for guidance
|
||||
|
||||
**Output During Implementation**
|
||||
|
||||
```
|
||||
## Implementing: <change-name> (schema: <schema-name>)
|
||||
|
||||
Working on task 3/7: <task description>
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
|
||||
Working on task 4/7: <task description>
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
```
|
||||
|
||||
**Output On Completion**
|
||||
|
||||
```
|
||||
## Implementation Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Progress:** 7/7 tasks complete ✓
|
||||
|
||||
### Completed This Session
|
||||
- [x] Task 1
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tasks complete! Ready to archive this change.
|
||||
```
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
|
||||
```
|
||||
## Implementation Paused
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Progress:** 4/7 tasks complete
|
||||
|
||||
### Issue Encountered
|
||||
<description of the issue>
|
||||
|
||||
**Options:**
|
||||
1. <option 1>
|
||||
2. <option 2>
|
||||
3. Other approach
|
||||
|
||||
What would you like to do?
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Keep going through tasks until done or blocked
|
||||
- Always read context files before starting (from the apply instructions output)
|
||||
- If task is ambiguous, pause and ask before implementing
|
||||
- If implementation reveals issues, pause and suggest artifact updates
|
||||
- Keep code changes minimal and scoped to each task
|
||||
- Update task checkbox immediately after completing each task
|
||||
- Pause on errors, blockers, or unclear requirements - don't guess
|
||||
- Use contextFiles from CLI output, don't assume specific file names
|
||||
|
||||
**Fluid Workflow Integration**
|
||||
|
||||
This skill supports the "actions on a change" model:
|
||||
|
||||
- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions
|
||||
- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly
|
||||
@@ -1,155 +0,0 @@
|
||||
---
|
||||
description: Archive a completed change in the experimental workflow
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
**Input**: Optionally specify a change name after `/opsx:archive` (e.g., `/opsx:archive add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
|
||||
Show only active changes (not already archived).
|
||||
Include the schema used for each change if available.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Check artifact completion status**
|
||||
|
||||
Run `openspec status --change "<name>" --json` to check artifact completion.
|
||||
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used
|
||||
- `artifacts`: List of artifacts with their status (`done` or other)
|
||||
|
||||
**If any artifacts are not `done`:**
|
||||
- Display warning listing incomplete artifacts
|
||||
- Prompt user for confirmation to continue
|
||||
- Proceed if user confirms
|
||||
|
||||
3. **Check task completion status**
|
||||
|
||||
Read the tasks file (typically `tasks.md`) to check for incomplete tasks.
|
||||
|
||||
Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).
|
||||
|
||||
**If incomplete tasks found:**
|
||||
- Display warning showing count of incomplete tasks
|
||||
- Prompt user for confirmation to continue
|
||||
- Proceed if user confirms
|
||||
|
||||
**If no tasks file exists:** Proceed without task-related warning.
|
||||
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
Check for delta specs at `openspec/changes/<name>/specs/`. If none exist, proceed without sync prompt.
|
||||
|
||||
**If delta specs exist:**
|
||||
- Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`
|
||||
- Determine what changes would be applied (adds, modifications, removals, renames)
|
||||
- Show a combined summary before prompting
|
||||
|
||||
**Prompt options:**
|
||||
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
||||
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
||||
|
||||
If user chooses sync, execute `/opsx:sync` logic. Proceed to archive regardless of choice.
|
||||
|
||||
5. **Perform the archive**
|
||||
|
||||
Create the archive directory if it doesn't exist:
|
||||
```bash
|
||||
mkdir -p openspec/changes/archive
|
||||
```
|
||||
|
||||
Generate target name using current date: `YYYY-MM-DD-<change-name>`
|
||||
|
||||
**Check if target already exists:**
|
||||
- If yes: Fail with error, suggest renaming existing archive or using different date
|
||||
- If no: Move the change directory to archive
|
||||
|
||||
```bash
|
||||
mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
|
||||
```
|
||||
|
||||
6. **Display summary**
|
||||
|
||||
Show archive completion summary including:
|
||||
- Change name
|
||||
- Schema that was used
|
||||
- Archive location
|
||||
- Spec sync status (synced / sync skipped / no delta specs)
|
||||
- Note about any warnings (incomplete artifacts/tasks)
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** ✓ Synced to main specs
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
```
|
||||
|
||||
**Output On Success (No Delta Specs)**
|
||||
|
||||
```
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** No delta specs
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
```
|
||||
|
||||
**Output On Success With Warnings**
|
||||
|
||||
```
|
||||
## Archive Complete (with warnings)
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
**Specs:** Sync skipped (user chose to skip)
|
||||
|
||||
**Warnings:**
|
||||
- Archived with 2 incomplete artifacts
|
||||
- Archived with 3 incomplete tasks
|
||||
- Delta spec sync was skipped (user chose to skip)
|
||||
|
||||
Review the archive if this was not intentional.
|
||||
```
|
||||
|
||||
**Output On Error (Archive Exists)**
|
||||
|
||||
```
|
||||
## Archive Failed
|
||||
|
||||
**Change:** <change-name>
|
||||
**Target:** openspec/changes/archive/YYYY-MM-DD-<name>/
|
||||
|
||||
Target archive directory already exists.
|
||||
|
||||
**Options:**
|
||||
1. Rename the existing archive
|
||||
2. Delete the existing archive if it's a duplicate
|
||||
3. Wait until a different date to archive
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Always prompt for change selection if not provided
|
||||
- Use artifact graph (openspec status --json) for completion checking
|
||||
- Don't block archive on warnings - just inform and confirm
|
||||
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
||||
- Show clear summary of what happened
|
||||
- If sync is requested, use /opsx:sync approach (agent-driven)
|
||||
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
||||
@@ -1,240 +0,0 @@
|
||||
---
|
||||
description: Archive multiple completed changes at once
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Archive multiple completed changes in a single operation.
|
||||
|
||||
This skill allows you to batch-archive changes, handling spec conflicts intelligently by checking the codebase to determine what's actually implemented.
|
||||
|
||||
**Input**: None required (prompts for selection)
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **Get active changes**
|
||||
|
||||
Run `openspec list --json` to get all active changes.
|
||||
|
||||
If no active changes exist, inform user and stop.
|
||||
|
||||
2. **Prompt for change selection**
|
||||
|
||||
Use **AskUserQuestion tool** with multi-select to let user choose changes:
|
||||
- Show each change with its schema
|
||||
- Include an option for "All changes"
|
||||
- Allow any number of selections (1+ works, 2+ is the typical use case)
|
||||
|
||||
**IMPORTANT**: Do NOT auto-select. Always let the user choose.
|
||||
|
||||
3. **Batch validation - gather status for all selected changes**
|
||||
|
||||
For each selected change, collect:
|
||||
|
||||
a. **Artifact status** - Run `openspec status --change "<name>" --json`
|
||||
- Parse `schemaName` and `artifacts` list
|
||||
- Note which artifacts are `done` vs other states
|
||||
|
||||
b. **Task completion** - Read `openspec/changes/<name>/tasks.md`
|
||||
- Count `- [ ]` (incomplete) vs `- [x]` (complete)
|
||||
- If no tasks file exists, note as "No tasks"
|
||||
|
||||
c. **Delta specs** - Check `openspec/changes/<name>/specs/` directory
|
||||
- List which capability specs exist
|
||||
- For each, extract requirement names (lines matching `### Requirement: <name>`)
|
||||
|
||||
4. **Detect spec conflicts**
|
||||
|
||||
Build a map of `capability -> [changes that touch it]`:
|
||||
|
||||
```
|
||||
auth -> [change-a, change-b] <- CONFLICT (2+ changes)
|
||||
api -> [change-c] <- OK (only 1 change)
|
||||
```
|
||||
|
||||
A conflict exists when 2+ selected changes have delta specs for the same capability.
|
||||
|
||||
5. **Resolve conflicts agentically**
|
||||
|
||||
**For each conflict**, investigate the codebase:
|
||||
|
||||
a. **Read the delta specs** from each conflicting change to understand what each claims to add/modify
|
||||
|
||||
b. **Search the codebase** for implementation evidence:
|
||||
- Look for code implementing requirements from each delta spec
|
||||
- Check for related files, functions, or tests
|
||||
|
||||
c. **Determine resolution**:
|
||||
- If only one change is actually implemented -> sync that one's specs
|
||||
- If both implemented -> apply in chronological order (older first, newer overwrites)
|
||||
- If neither implemented -> skip spec sync, warn user
|
||||
|
||||
d. **Record resolution** for each conflict:
|
||||
- Which change's specs to apply
|
||||
- In what order (if both)
|
||||
- Rationale (what was found in codebase)
|
||||
|
||||
6. **Show consolidated status table**
|
||||
|
||||
Display a table summarizing all changes:
|
||||
|
||||
```
|
||||
| Change | Artifacts | Tasks | Specs | Conflicts | Status |
|
||||
|---------------------|-----------|-------|---------|-----------|--------|
|
||||
| schema-management | Done | 5/5 | 2 delta | None | Ready |
|
||||
| project-config | Done | 3/3 | 1 delta | None | Ready |
|
||||
| add-oauth | Done | 4/4 | 1 delta | auth (!) | Ready* |
|
||||
| add-verify-skill | 1 left | 2/5 | None | None | Warn |
|
||||
```
|
||||
|
||||
For conflicts, show the resolution:
|
||||
```
|
||||
* Conflict resolution:
|
||||
- auth spec: Will apply add-oauth then add-jwt (both implemented, chronological order)
|
||||
```
|
||||
|
||||
For incomplete changes, show warnings:
|
||||
```
|
||||
Warnings:
|
||||
- add-verify-skill: 1 incomplete artifact, 3 incomplete tasks
|
||||
```
|
||||
|
||||
7. **Confirm batch operation**
|
||||
|
||||
Use **AskUserQuestion tool** with a single confirmation:
|
||||
|
||||
- "Archive N changes?" with options based on status
|
||||
- Options might include:
|
||||
- "Archive all N changes"
|
||||
- "Archive only N ready changes (skip incomplete)"
|
||||
- "Cancel"
|
||||
|
||||
If there are incomplete changes, make clear they'll be archived with warnings.
|
||||
|
||||
8. **Execute archive for each confirmed change**
|
||||
|
||||
Process changes in the determined order (respecting conflict resolution):
|
||||
|
||||
a. **Sync specs** if delta specs exist:
|
||||
- Use the openspec-sync-specs approach (agent-driven intelligent merge)
|
||||
- For conflicts, apply in resolved order
|
||||
- Track if sync was done
|
||||
|
||||
b. **Perform the archive**:
|
||||
```bash
|
||||
mkdir -p openspec/changes/archive
|
||||
mv openspec/changes/<name> openspec/changes/archive/YYYY-MM-DD-<name>
|
||||
```
|
||||
|
||||
c. **Track outcome** for each change:
|
||||
- Success: archived successfully
|
||||
- Failed: error during archive (record error)
|
||||
- Skipped: user chose not to archive (if applicable)
|
||||
|
||||
9. **Display summary**
|
||||
|
||||
Show final results:
|
||||
|
||||
```
|
||||
## Bulk Archive Complete
|
||||
|
||||
Archived 3 changes:
|
||||
- schema-management-cli -> archive/2026-01-19-schema-management-cli/
|
||||
- project-config -> archive/2026-01-19-project-config/
|
||||
- add-oauth -> archive/2026-01-19-add-oauth/
|
||||
|
||||
Skipped 1 change:
|
||||
- add-verify-skill (user chose not to archive incomplete)
|
||||
|
||||
Spec sync summary:
|
||||
- 4 delta specs synced to main specs
|
||||
- 1 conflict resolved (auth: applied both in chronological order)
|
||||
```
|
||||
|
||||
If any failures:
|
||||
```
|
||||
Failed 1 change:
|
||||
- some-change: Archive directory already exists
|
||||
```
|
||||
|
||||
**Conflict Resolution Examples**
|
||||
|
||||
Example 1: Only one implemented
|
||||
```
|
||||
Conflict: specs/auth/spec.md touched by [add-oauth, add-jwt]
|
||||
|
||||
Checking add-oauth:
|
||||
- Delta adds "OAuth Provider Integration" requirement
|
||||
- Searching codebase... found src/auth/oauth.ts implementing OAuth flow
|
||||
|
||||
Checking add-jwt:
|
||||
- Delta adds "JWT Token Handling" requirement
|
||||
- Searching codebase... no JWT implementation found
|
||||
|
||||
Resolution: Only add-oauth is implemented. Will sync add-oauth specs only.
|
||||
```
|
||||
|
||||
Example 2: Both implemented
|
||||
```
|
||||
Conflict: specs/api/spec.md touched by [add-rest-api, add-graphql]
|
||||
|
||||
Checking add-rest-api (created 2026-01-10):
|
||||
- Delta adds "REST Endpoints" requirement
|
||||
- Searching codebase... found src/api/rest.ts
|
||||
|
||||
Checking add-graphql (created 2026-01-15):
|
||||
- Delta adds "GraphQL Schema" requirement
|
||||
- Searching codebase... found src/api/graphql.ts
|
||||
|
||||
Resolution: Both implemented. Will apply add-rest-api specs first,
|
||||
then add-graphql specs (chronological order, newer takes precedence).
|
||||
```
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
## Bulk Archive Complete
|
||||
|
||||
Archived N changes:
|
||||
- <change-1> -> archive/YYYY-MM-DD-<change-1>/
|
||||
- <change-2> -> archive/YYYY-MM-DD-<change-2>/
|
||||
|
||||
Spec sync summary:
|
||||
- N delta specs synced to main specs
|
||||
- No conflicts (or: M conflicts resolved)
|
||||
```
|
||||
|
||||
**Output On Partial Success**
|
||||
|
||||
```
|
||||
## Bulk Archive Complete (partial)
|
||||
|
||||
Archived N changes:
|
||||
- <change-1> -> archive/YYYY-MM-DD-<change-1>/
|
||||
|
||||
Skipped M changes:
|
||||
- <change-2> (user chose not to archive incomplete)
|
||||
|
||||
Failed K changes:
|
||||
- <change-3>: Archive directory already exists
|
||||
```
|
||||
|
||||
**Output When No Changes**
|
||||
|
||||
```
|
||||
## No Changes to Archive
|
||||
|
||||
No active changes found. Use `/opsx:new` to create a new change.
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Allow any number of changes (1+ is fine, 2+ is the typical use case)
|
||||
- Always prompt for selection, never auto-select
|
||||
- Detect spec conflicts early and resolve by checking codebase
|
||||
- When both changes are implemented, apply specs in chronological order
|
||||
- Skip spec sync only when implementation is missing (warn user)
|
||||
- Show clear per-change status before confirming
|
||||
- Use single confirmation for entire batch
|
||||
- Track and report all outcomes (success/skip/fail)
|
||||
- Preserve .openspec.yaml when moving to archive
|
||||
- Archive directory target uses current date: YYYY-MM-DD-<name>
|
||||
- If archive target exists, fail that change but continue with others
|
||||
@@ -1,112 +0,0 @@
|
||||
---
|
||||
description: Continue working on a change - create the next artifact (Experimental)
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Continue working on a change by creating the next artifact.
|
||||
|
||||
**Input**: Optionally specify a change name after `/opsx:continue` (e.g., `/opsx:continue add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes sorted by most recently modified. Then use the **AskUserQuestion tool** to let the user select which change to work on.
|
||||
|
||||
Present the top 3-4 most recently modified changes as options, showing:
|
||||
- Change name
|
||||
- Schema (from `schema` field if present, otherwise "spec-driven")
|
||||
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
||||
- How recently it was modified (from `lastModified` field)
|
||||
|
||||
Mark the most recently modified change as "(Recommended)" since it's likely what the user wants to continue.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Check current status**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand current state. The response includes:
|
||||
- `schemaName`: The workflow schema being used (e.g., "spec-driven")
|
||||
- `artifacts`: Array of artifacts with their status ("done", "ready", "blocked")
|
||||
- `isComplete`: Boolean indicating if all artifacts are complete
|
||||
|
||||
3. **Act based on status**:
|
||||
|
||||
---
|
||||
|
||||
**If all artifacts are complete (`isComplete: true`)**:
|
||||
- Congratulate the user
|
||||
- Show final status including the schema used
|
||||
- Suggest: "All artifacts created! You can now implement this change or archive it."
|
||||
- STOP
|
||||
|
||||
---
|
||||
|
||||
**If artifacts are ready to create** (status shows artifacts with `status: "ready"`):
|
||||
- Pick the FIRST artifact with `status: "ready"` from the status output
|
||||
- Get its instructions:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- Parse the JSON. The key fields are:
|
||||
- `context`: Project background (constraints for you - do NOT include in output)
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance
|
||||
- `outputPath`: Where to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- **Create the artifact file**:
|
||||
- Read any completed dependency files for context
|
||||
- Use `template` as the structure - fill in its sections
|
||||
- Apply `context` and `rules` as constraints when writing - but do NOT copy them into the file
|
||||
- Write to the output path specified in instructions
|
||||
- Show what was created and what's now unlocked
|
||||
- STOP after creating ONE artifact
|
||||
|
||||
---
|
||||
|
||||
**If no artifacts are ready (all blocked)**:
|
||||
- This shouldn't happen with a valid schema
|
||||
- Show status and suggest checking for issues
|
||||
|
||||
4. **After creating an artifact, show progress**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
|
||||
**Output**
|
||||
|
||||
After each invocation, show:
|
||||
- Which artifact was created
|
||||
- Schema workflow being used
|
||||
- Current progress (N/M complete)
|
||||
- What artifacts are now unlocked
|
||||
- Prompt: "Run `/opsx:continue` to create the next artifact"
|
||||
|
||||
**Artifact Creation Guidelines**
|
||||
|
||||
The artifact types and their purpose depend on the schema. Use the `instruction` field from the instructions output to understand what to create.
|
||||
|
||||
Common artifact patterns:
|
||||
|
||||
**spec-driven schema** (proposal → specs → design → tasks):
|
||||
- **proposal.md**: Ask user about the change if not clear. Fill in Why, What Changes, Capabilities, Impact.
|
||||
- The Capabilities section is critical - each capability listed will need a spec file.
|
||||
- **specs/<capability>/spec.md**: Create one spec per capability listed in the proposal's Capabilities section (use the capability name, not the change name).
|
||||
- **design.md**: Document technical decisions, architecture, and implementation approach.
|
||||
- **tasks.md**: Break down implementation into checkboxed tasks.
|
||||
|
||||
For other schemas, follow the `instruction` field from the CLI output.
|
||||
|
||||
**Guardrails**
|
||||
- Create ONE artifact per invocation
|
||||
- Always read dependency artifacts before creating a new one
|
||||
- Never skip artifacts or create out of order
|
||||
- If context is unclear, ask the user before creating
|
||||
- Verify the artifact file exists after writing before marking progress
|
||||
- Use the schema's artifact sequence, don't assume specific artifact names
|
||||
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
||||
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
||||
- These guide what you write, but should never appear in the output
|
||||
@@ -1,172 +0,0 @@
|
||||
---
|
||||
description: Enter explore mode - think through ideas, investigate problems, clarify requirements
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
|
||||
|
||||
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER write code or implement features. If the user asks you to implement something, remind them to exit explore mode first (e.g., start a change with `/opsx:new` or `/opsx:ff`). You MAY create OpenSpec artifacts (proposals, designs, specs) if the user asks—that's capturing thinking, not implementing.
|
||||
|
||||
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
||||
|
||||
**Input**: The argument after `/opsx:explore` is whatever the user wants to think about. Could be:
|
||||
- A vague idea: "real-time collaboration"
|
||||
- A specific problem: "the auth system is getting unwieldy"
|
||||
- A change name: "add-dark-mode" (to explore in context of that change)
|
||||
- A comparison: "postgres vs sqlite for this"
|
||||
- Nothing (just enter explore mode)
|
||||
|
||||
---
|
||||
|
||||
## The Stance
|
||||
|
||||
- **Curious, not prescriptive** - Ask questions that emerge naturally, don't follow a script
|
||||
- **Open threads, not interrogations** - Surface multiple interesting directions and let the user follow what resonates. Don't funnel them through a single path of questions.
|
||||
- **Visual** - Use ASCII diagrams liberally when they'd help clarify thinking
|
||||
- **Adaptive** - Follow interesting threads, pivot when new information emerges
|
||||
- **Patient** - Don't rush to conclusions, let the shape of the problem emerge
|
||||
- **Grounded** - Explore the actual codebase when relevant, don't just theorize
|
||||
|
||||
---
|
||||
|
||||
## What You Might Do
|
||||
|
||||
Depending on what the user brings, you might:
|
||||
|
||||
**Explore the problem space**
|
||||
- Ask clarifying questions that emerge from what they said
|
||||
- Challenge assumptions
|
||||
- Reframe the problem
|
||||
- Find analogies
|
||||
|
||||
**Investigate the codebase**
|
||||
- Map existing architecture relevant to the discussion
|
||||
- Find integration points
|
||||
- Identify patterns already in use
|
||||
- Surface hidden complexity
|
||||
|
||||
**Compare options**
|
||||
- Brainstorm multiple approaches
|
||||
- Build comparison tables
|
||||
- Sketch tradeoffs
|
||||
- Recommend a path (if asked)
|
||||
|
||||
**Visualize**
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Use ASCII diagrams liberally │
|
||||
├─────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────┐ ┌────────┐ │
|
||||
│ │ State │────────▶│ State │ │
|
||||
│ │ A │ │ B │ │
|
||||
│ └────────┘ └────────┘ │
|
||||
│ │
|
||||
│ System diagrams, state machines, │
|
||||
│ data flows, architecture sketches, │
|
||||
│ dependency graphs, comparison tables │
|
||||
│ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Surface risks and unknowns**
|
||||
- Identify what could go wrong
|
||||
- Find gaps in understanding
|
||||
- Suggest spikes or investigations
|
||||
|
||||
---
|
||||
|
||||
## OpenSpec Awareness
|
||||
|
||||
You have full context of the OpenSpec system. Use it naturally, don't force it.
|
||||
|
||||
### Check for context
|
||||
|
||||
At the start, quickly check what exists:
|
||||
```bash
|
||||
openspec list --json
|
||||
```
|
||||
|
||||
This tells you:
|
||||
- If there are active changes
|
||||
- Their names, schemas, and status
|
||||
- What the user might be working on
|
||||
|
||||
If the user mentioned a specific change name, read its artifacts for context.
|
||||
|
||||
### When no change exists
|
||||
|
||||
Think freely. When insights crystallize, you might offer:
|
||||
|
||||
- "This feels solid enough to start a change. Want me to create one?"
|
||||
→ Can transition to `/opsx:new` or `/opsx:ff`
|
||||
- Or keep exploring - no pressure to formalize
|
||||
|
||||
### When a change exists
|
||||
|
||||
If the user mentions a change or you detect one is relevant:
|
||||
|
||||
1. **Read existing artifacts for context**
|
||||
- `openspec/changes/<name>/proposal.md`
|
||||
- `openspec/changes/<name>/design.md`
|
||||
- `openspec/changes/<name>/tasks.md`
|
||||
- etc.
|
||||
|
||||
2. **Reference them naturally in conversation**
|
||||
- "Your design mentions using Redis, but we just realized SQLite fits better..."
|
||||
- "The proposal scopes this to premium users, but we're now thinking everyone..."
|
||||
|
||||
3. **Offer to capture when decisions are made**
|
||||
|
||||
| Insight Type | Where to Capture |
|
||||
|--------------|------------------|
|
||||
| New requirement discovered | `specs/<capability>/spec.md` |
|
||||
| Requirement changed | `specs/<capability>/spec.md` |
|
||||
| Design decision made | `design.md` |
|
||||
| Scope changed | `proposal.md` |
|
||||
| New work identified | `tasks.md` |
|
||||
| Assumption invalidated | Relevant artifact |
|
||||
|
||||
Example offers:
|
||||
- "That's a design decision. Capture it in design.md?"
|
||||
- "This is a new requirement. Add it to specs?"
|
||||
- "This changes scope. Update the proposal?"
|
||||
|
||||
4. **The user decides** - Offer and move on. Don't pressure. Don't auto-capture.
|
||||
|
||||
---
|
||||
|
||||
## What You Don't Have To Do
|
||||
|
||||
- Follow a script
|
||||
- Ask the same questions every time
|
||||
- Produce a specific artifact
|
||||
- Reach a conclusion
|
||||
- Stay on topic if a tangent is valuable
|
||||
- Be brief (this is thinking time)
|
||||
|
||||
---
|
||||
|
||||
## Ending Discovery
|
||||
|
||||
There's no required ending. Discovery might:
|
||||
|
||||
- **Flow into action**: "Ready to start? `/opsx:new` or `/opsx:ff`"
|
||||
- **Result in artifact updates**: "Updated design.md with these decisions"
|
||||
- **Just provide clarity**: User has what they need, moves on
|
||||
- **Continue later**: "We can pick this up anytime"
|
||||
|
||||
When things crystallize, you might offer a summary - but it's optional. Sometimes the thinking IS the value.
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Don't implement** - Never write code or implement features. Creating OpenSpec artifacts is fine, writing application code is not.
|
||||
- **Don't fake understanding** - If something is unclear, dig deeper
|
||||
- **Don't rush** - Discovery is thinking time, not task time
|
||||
- **Don't force structure** - Let patterns emerge naturally
|
||||
- **Don't auto-capture** - Offer to save insights, don't just do it
|
||||
- **Do visualize** - A good diagram is worth many paragraphs
|
||||
- **Do explore the codebase** - Ground discussions in reality
|
||||
- **Do question assumptions** - Including the user's and your own
|
||||
@@ -1,92 +0,0 @@
|
||||
---
|
||||
description: Create a change and generate all artifacts needed for implementation in one go
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Fast-forward through artifact creation - generate everything needed to start implementation.
|
||||
|
||||
**Input**: The argument after `/opsx:ff` is the change name (kebab-case), OR a description of what the user wants to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no input provided, ask what they want to build**
|
||||
|
||||
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
2. **Create the change directory**
|
||||
```bash
|
||||
openspec new change "<name>"
|
||||
```
|
||||
This creates a scaffolded change at `openspec/changes/<name>/`.
|
||||
|
||||
3. **Get the artifact build order**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to get:
|
||||
- `applyRequires`: array of artifact IDs needed before implementation (e.g., `["tasks"]`)
|
||||
- `artifacts`: list of all artifacts with their status and dependencies
|
||||
|
||||
4. **Create artifacts in sequence until apply-ready**
|
||||
|
||||
Use the **TodoWrite tool** to track progress through the artifacts.
|
||||
|
||||
Loop through artifacts in dependency order (artifacts with no pending dependencies first):
|
||||
|
||||
a. **For each artifact that is `ready` (dependencies satisfied)**:
|
||||
- Get instructions:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- The instructions JSON includes:
|
||||
- `context`: Project background (constraints for you - do NOT include in output)
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance for this artifact type
|
||||
- `outputPath`: Where to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- Read any completed dependency files for context
|
||||
- Create the artifact file using `template` as the structure
|
||||
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
||||
- Show brief progress: "✓ Created <artifact-id>"
|
||||
|
||||
b. **Continue until all `applyRequires` artifacts are complete**
|
||||
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
||||
- Check if every artifact ID in `applyRequires` has `status: "done"` in the artifacts array
|
||||
- Stop when all `applyRequires` artifacts are done
|
||||
|
||||
c. **If an artifact requires user input** (unclear context):
|
||||
- Use **AskUserQuestion tool** to clarify
|
||||
- Then continue with creation
|
||||
|
||||
5. **Show final status**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
|
||||
**Output**
|
||||
|
||||
After completing all artifacts, summarize:
|
||||
- Change name and location
|
||||
- List of artifacts created with brief descriptions
|
||||
- What's ready: "All artifacts created! Ready for implementation."
|
||||
- Prompt: "Run `/opsx:apply` to start implementing."
|
||||
|
||||
**Artifact Creation Guidelines**
|
||||
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type
|
||||
- The schema defines what each artifact should contain - follow it
|
||||
- Read dependency artifacts for context before creating new ones
|
||||
- Use the `template` as a starting point, filling in based on context
|
||||
|
||||
**Guardrails**
|
||||
- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)
|
||||
- Always read dependency artifacts before creating a new one
|
||||
- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum
|
||||
- If a change with that name already exists, ask if user wants to continue it or create a new one
|
||||
- Verify each artifact file exists after writing before proceeding to next
|
||||
@@ -1,67 +0,0 @@
|
||||
---
|
||||
description: Start a new change using the experimental artifact workflow (OPSX)
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Start a new change using the experimental artifact-driven approach.
|
||||
|
||||
**Input**: The argument after `/opsx:new` is the change name (kebab-case), OR a description of what the user wants to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no input provided, ask what they want to build**
|
||||
|
||||
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
2. **Determine the workflow schema**
|
||||
|
||||
Use the default schema (omit `--schema`) unless the user explicitly requests a different workflow.
|
||||
|
||||
**Use a different schema only if the user mentions:**
|
||||
- A specific schema name → use `--schema <name>`
|
||||
- "show workflows" or "what workflows" → run `openspec schemas --json` and let them choose
|
||||
|
||||
**Otherwise**: Omit `--schema` to use the default.
|
||||
|
||||
3. **Create the change directory**
|
||||
```bash
|
||||
openspec new change "<name>"
|
||||
```
|
||||
Add `--schema <name>` only if the user requested a specific workflow.
|
||||
This creates a scaffolded change at `openspec/changes/<name>/` with the selected schema.
|
||||
|
||||
4. **Show the artifact status**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
This shows which artifacts need to be created and which are ready (dependencies satisfied).
|
||||
|
||||
5. **Get instructions for the first artifact**
|
||||
The first artifact depends on the schema. Check the status output to find the first artifact with status "ready".
|
||||
```bash
|
||||
openspec instructions <first-artifact-id> --change "<name>"
|
||||
```
|
||||
This outputs the template and context for creating the first artifact.
|
||||
|
||||
6. **STOP and wait for user direction**
|
||||
|
||||
**Output**
|
||||
|
||||
After completing the steps, summarize:
|
||||
- Change name and location
|
||||
- Schema/workflow being used and its artifact sequence
|
||||
- Current status (0/N artifacts complete)
|
||||
- The template for the first artifact
|
||||
- Prompt: "Ready to create the first artifact? Run `/opsx:continue` or just describe what this change is about and I'll draft it."
|
||||
|
||||
**Guardrails**
|
||||
- Do NOT create any artifacts yet - just show the instructions
|
||||
- Do NOT advance beyond showing the first artifact template
|
||||
- If the name is invalid (not kebab-case), ask for a valid name
|
||||
- If a change with that name already exists, suggest using `/opsx:continue` instead
|
||||
- Pass --schema if using a non-default workflow
|
||||
@@ -1,523 +0,0 @@
|
||||
---
|
||||
description: Guided onboarding - walk through a complete OpenSpec workflow cycle with narration
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Guide the user through their first complete OpenSpec workflow cycle. This is a teaching experience—you'll do real work in their codebase while explaining each step.
|
||||
|
||||
---
|
||||
|
||||
## Preflight
|
||||
|
||||
Before starting, check if OpenSpec is initialized:
|
||||
|
||||
```bash
|
||||
openspec status --json 2>&1 || echo "NOT_INITIALIZED"
|
||||
```
|
||||
|
||||
**If not initialized:**
|
||||
> OpenSpec isn't set up in this project yet. Run `openspec init` first, then come back to `/opsx:onboard`.
|
||||
|
||||
Stop here if not initialized.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Welcome
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
## Welcome to OpenSpec!
|
||||
|
||||
I'll walk you through a complete change cycle—from idea to implementation—using a real task in your codebase. Along the way, you'll learn the workflow by doing it.
|
||||
|
||||
**What we'll do:**
|
||||
1. Pick a small, real task in your codebase
|
||||
2. Explore the problem briefly
|
||||
3. Create a change (the container for our work)
|
||||
4. Build the artifacts: proposal → specs → design → tasks
|
||||
5. Implement the tasks
|
||||
6. Archive the completed change
|
||||
|
||||
**Time:** ~15-20 minutes
|
||||
|
||||
Let's start by finding something to work on.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Task Selection
|
||||
|
||||
### Codebase Analysis
|
||||
|
||||
Scan the codebase for small improvement opportunities. Look for:
|
||||
|
||||
1. **TODO/FIXME comments** - Search for `TODO`, `FIXME`, `HACK`, `XXX` in code files
|
||||
2. **Missing error handling** - `catch` blocks that swallow errors, risky operations without try-catch
|
||||
3. **Functions without tests** - Cross-reference `src/` with test directories
|
||||
4. **Type issues** - `any` types in TypeScript files (`: any`, `as any`)
|
||||
5. **Debug artifacts** - `console.log`, `console.debug`, `debugger` statements in non-debug code
|
||||
6. **Missing validation** - User input handlers without validation
|
||||
|
||||
Also check recent git activity:
|
||||
```bash
|
||||
git log --oneline -10 2>/dev/null || echo "No git history"
|
||||
```
|
||||
|
||||
### Present Suggestions
|
||||
|
||||
From your analysis, present 3-4 specific suggestions:
|
||||
|
||||
```
|
||||
## Task Suggestions
|
||||
|
||||
Based on scanning your codebase, here are some good starter tasks:
|
||||
|
||||
**1. [Most promising task]**
|
||||
Location: `src/path/to/file.ts:42`
|
||||
Scope: ~1-2 files, ~20-30 lines
|
||||
Why it's good: [brief reason]
|
||||
|
||||
**2. [Second task]**
|
||||
Location: `src/another/file.ts`
|
||||
Scope: ~1 file, ~15 lines
|
||||
Why it's good: [brief reason]
|
||||
|
||||
**3. [Third task]**
|
||||
Location: [location]
|
||||
Scope: [estimate]
|
||||
Why it's good: [brief reason]
|
||||
|
||||
**4. Something else?**
|
||||
Tell me what you'd like to work on.
|
||||
|
||||
Which task interests you? (Pick a number or describe your own)
|
||||
```
|
||||
|
||||
**If nothing found:** Fall back to asking what the user wants to build:
|
||||
> I didn't find obvious quick wins in your codebase. What's something small you've been meaning to add or fix?
|
||||
|
||||
### Scope Guardrail
|
||||
|
||||
If the user picks or describes something too large (major feature, multi-day work):
|
||||
|
||||
```
|
||||
That's a valuable task, but it's probably larger than ideal for your first OpenSpec run-through.
|
||||
|
||||
For learning the workflow, smaller is better—it lets you see the full cycle without getting stuck in implementation details.
|
||||
|
||||
**Options:**
|
||||
1. **Slice it smaller** - What's the smallest useful piece of [their task]? Maybe just [specific slice]?
|
||||
2. **Pick something else** - One of the other suggestions, or a different small task?
|
||||
3. **Do it anyway** - If you really want to tackle this, we can. Just know it'll take longer.
|
||||
|
||||
What would you prefer?
|
||||
```
|
||||
|
||||
Let the user override if they insist—this is a soft guardrail.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Explore Demo
|
||||
|
||||
Once a task is selected, briefly demonstrate explore mode:
|
||||
|
||||
```
|
||||
Before we create a change, let me quickly show you **explore mode**—it's how you think through problems before committing to a direction.
|
||||
```
|
||||
|
||||
Spend 1-2 minutes investigating the relevant code:
|
||||
- Read the file(s) involved
|
||||
- Draw a quick ASCII diagram if it helps
|
||||
- Note any considerations
|
||||
|
||||
```
|
||||
## Quick Exploration
|
||||
|
||||
[Your brief analysis—what you found, any considerations]
|
||||
|
||||
┌─────────────────────────────────────────┐
|
||||
│ [Optional: ASCII diagram if helpful] │
|
||||
└─────────────────────────────────────────┘
|
||||
|
||||
Explore mode (`/opsx:explore`) is for this kind of thinking—investigating before implementing. You can use it anytime you need to think through a problem.
|
||||
|
||||
Now let's create a change to hold our work.
|
||||
```
|
||||
|
||||
**PAUSE** - Wait for user acknowledgment before proceeding.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Create the Change
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Creating a Change
|
||||
|
||||
A "change" in OpenSpec is a container for all the thinking and planning around a piece of work. It lives in `openspec/changes/<name>/` and holds your artifacts—proposal, specs, design, tasks.
|
||||
|
||||
Let me create one for our task.
|
||||
```
|
||||
|
||||
**DO:** Create the change with a derived kebab-case name:
|
||||
```bash
|
||||
openspec new change "<derived-name>"
|
||||
```
|
||||
|
||||
**SHOW:**
|
||||
```
|
||||
Created: `openspec/changes/<name>/`
|
||||
|
||||
The folder structure:
|
||||
```
|
||||
openspec/changes/<name>/
|
||||
├── proposal.md ← Why we're doing this (empty, we'll fill it)
|
||||
├── design.md ← How we'll build it (empty)
|
||||
├── specs/ ← Detailed requirements (empty)
|
||||
└── tasks.md ← Implementation checklist (empty)
|
||||
```
|
||||
|
||||
Now let's fill in the first artifact—the proposal.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Proposal
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## The Proposal
|
||||
|
||||
The proposal captures **why** we're making this change and **what** it involves at a high level. It's the "elevator pitch" for the work.
|
||||
|
||||
I'll draft one based on our task.
|
||||
```
|
||||
|
||||
**DO:** Draft the proposal content (don't save yet):
|
||||
|
||||
```
|
||||
Here's a draft proposal:
|
||||
|
||||
---
|
||||
|
||||
## Why
|
||||
|
||||
[1-2 sentences explaining the problem/opportunity]
|
||||
|
||||
## What Changes
|
||||
|
||||
[Bullet points of what will be different]
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `<capability-name>`: [brief description]
|
||||
|
||||
### Modified Capabilities
|
||||
<!-- If modifying existing behavior -->
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/path/to/file.ts`: [what changes]
|
||||
- [other files if applicable]
|
||||
|
||||
---
|
||||
|
||||
Does this capture the intent? I can adjust before we save it.
|
||||
```
|
||||
|
||||
**PAUSE** - Wait for user approval/feedback.
|
||||
|
||||
After approval, save the proposal:
|
||||
```bash
|
||||
openspec instructions proposal --change "<name>" --json
|
||||
```
|
||||
Then write the content to `openspec/changes/<name>/proposal.md`.
|
||||
|
||||
```
|
||||
Proposal saved. This is your "why" document—you can always come back and refine it as understanding evolves.
|
||||
|
||||
Next up: specs.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Specs
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Specs
|
||||
|
||||
Specs define **what** we're building in precise, testable terms. They use a requirement/scenario format that makes expected behavior crystal clear.
|
||||
|
||||
For a small task like this, we might only need one spec file.
|
||||
```
|
||||
|
||||
**DO:** Create the spec file:
|
||||
```bash
|
||||
mkdir -p openspec/changes/<name>/specs/<capability-name>
|
||||
```
|
||||
|
||||
Draft the spec content:
|
||||
|
||||
```
|
||||
Here's the spec:
|
||||
|
||||
---
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: <Name>
|
||||
|
||||
<Description of what the system should do>
|
||||
|
||||
#### Scenario: <Scenario name>
|
||||
|
||||
- **WHEN** <trigger condition>
|
||||
- **THEN** <expected outcome>
|
||||
- **AND** <additional outcome if needed>
|
||||
|
||||
---
|
||||
|
||||
This format—WHEN/THEN/AND—makes requirements testable. You can literally read them as test cases.
|
||||
```
|
||||
|
||||
Save to `openspec/changes/<name>/specs/<capability>/spec.md`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: Design
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Design
|
||||
|
||||
The design captures **how** we'll build it—technical decisions, tradeoffs, approach.
|
||||
|
||||
For small changes, this might be brief. That's fine—not every change needs deep design discussion.
|
||||
```
|
||||
|
||||
**DO:** Draft design.md:
|
||||
|
||||
```
|
||||
Here's the design:
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
[Brief context about the current state]
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- [What we're trying to achieve]
|
||||
|
||||
**Non-Goals:**
|
||||
- [What's explicitly out of scope]
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision 1: [Key decision]
|
||||
|
||||
[Explanation of approach and rationale]
|
||||
|
||||
---
|
||||
|
||||
For a small task, this captures the key decisions without over-engineering.
|
||||
```
|
||||
|
||||
Save to `openspec/changes/<name>/design.md`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 8: Tasks
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Tasks
|
||||
|
||||
Finally, we break the work into implementation tasks—checkboxes that drive the apply phase.
|
||||
|
||||
These should be small, clear, and in logical order.
|
||||
```
|
||||
|
||||
**DO:** Generate tasks based on specs and design:
|
||||
|
||||
```
|
||||
Here are the implementation tasks:
|
||||
|
||||
---
|
||||
|
||||
## 1. [Category or file]
|
||||
|
||||
- [ ] 1.1 [Specific task]
|
||||
- [ ] 1.2 [Specific task]
|
||||
|
||||
## 2. Verify
|
||||
|
||||
- [ ] 2.1 [Verification step]
|
||||
|
||||
---
|
||||
|
||||
Each checkbox becomes a unit of work in the apply phase. Ready to implement?
|
||||
```
|
||||
|
||||
**PAUSE** - Wait for user to confirm they're ready to implement.
|
||||
|
||||
Save to `openspec/changes/<name>/tasks.md`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 9: Apply (Implementation)
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Implementation
|
||||
|
||||
Now we implement each task, checking them off as we go. I'll announce each one and occasionally note how the specs/design informed the approach.
|
||||
```
|
||||
|
||||
**DO:** For each task:
|
||||
|
||||
1. Announce: "Working on task N: [description]"
|
||||
2. Implement the change in the codebase
|
||||
3. Reference specs/design naturally: "The spec says X, so I'm doing Y"
|
||||
4. Mark complete in tasks.md: `- [ ]` → `- [x]`
|
||||
5. Brief status: "✓ Task N complete"
|
||||
|
||||
Keep narration light—don't over-explain every line of code.
|
||||
|
||||
After all tasks:
|
||||
|
||||
```
|
||||
## Implementation Complete
|
||||
|
||||
All tasks done:
|
||||
- [x] Task 1
|
||||
- [x] Task 2
|
||||
- [x] ...
|
||||
|
||||
The change is implemented! One more step—let's archive it.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 10: Archive
|
||||
|
||||
**EXPLAIN:**
|
||||
```
|
||||
## Archiving
|
||||
|
||||
When a change is complete, we archive it. This moves it from `openspec/changes/` to `openspec/changes/archive/YYYY-MM-DD-<name>/`.
|
||||
|
||||
Archived changes become your project's decision history—you can always find them later to understand why something was built a certain way.
|
||||
```
|
||||
|
||||
**DO:**
|
||||
```bash
|
||||
openspec archive "<name>"
|
||||
```
|
||||
|
||||
**SHOW:**
|
||||
```
|
||||
Archived to: `openspec/changes/archive/YYYY-MM-DD-<name>/`
|
||||
|
||||
The change is now part of your project's history. The code is in your codebase, the decision record is preserved.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 11: Recap & Next Steps
|
||||
|
||||
```
|
||||
## Congratulations!
|
||||
|
||||
You just completed a full OpenSpec cycle:
|
||||
|
||||
1. **Explore** - Thought through the problem
|
||||
2. **New** - Created a change container
|
||||
3. **Proposal** - Captured WHY
|
||||
4. **Specs** - Defined WHAT in detail
|
||||
5. **Design** - Decided HOW
|
||||
6. **Tasks** - Broke it into steps
|
||||
7. **Apply** - Implemented the work
|
||||
8. **Archive** - Preserved the record
|
||||
|
||||
This same rhythm works for any size change—a small fix or a major feature.
|
||||
|
||||
---
|
||||
|
||||
## Command Reference
|
||||
|
||||
| Command | What it does |
|
||||
|---------|--------------|
|
||||
| `/opsx:explore` | Think through problems before/during work |
|
||||
| `/opsx:new` | Start a new change, step through artifacts |
|
||||
| `/opsx:ff` | Fast-forward: create all artifacts at once |
|
||||
| `/opsx:continue` | Continue working on an existing change |
|
||||
| `/opsx:apply` | Implement tasks from a change |
|
||||
| `/opsx:verify` | Verify implementation matches artifacts |
|
||||
| `/opsx:archive` | Archive a completed change |
|
||||
|
||||
---
|
||||
|
||||
## What's Next?
|
||||
|
||||
Try `/opsx:new` or `/opsx:ff` on something you actually want to build. You've got the rhythm now!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Graceful Exit Handling
|
||||
|
||||
### User wants to stop mid-way
|
||||
|
||||
If the user says they need to stop, want to pause, or seem disengaged:
|
||||
|
||||
```
|
||||
No problem! Your change is saved at `openspec/changes/<name>/`.
|
||||
|
||||
To pick up where we left off later:
|
||||
- `/opsx:continue <name>` - Resume artifact creation
|
||||
- `/opsx:apply <name>` - Jump to implementation (if tasks exist)
|
||||
|
||||
The work won't be lost. Come back whenever you're ready.
|
||||
```
|
||||
|
||||
Exit gracefully without pressure.
|
||||
|
||||
### User just wants command reference
|
||||
|
||||
If the user says they just want to see the commands or skip the tutorial:
|
||||
|
||||
```
|
||||
## OpenSpec Quick Reference
|
||||
|
||||
| Command | What it does |
|
||||
|---------|--------------|
|
||||
| `/opsx:explore` | Think through problems (no code changes) |
|
||||
| `/opsx:new <name>` | Start a new change, step by step |
|
||||
| `/opsx:ff <name>` | Fast-forward: all artifacts at once |
|
||||
| `/opsx:continue <name>` | Continue an existing change |
|
||||
| `/opsx:apply <name>` | Implement tasks |
|
||||
| `/opsx:verify <name>` | Verify implementation |
|
||||
| `/opsx:archive <name>` | Archive when done |
|
||||
|
||||
Try `/opsx:new` to start your first change, or `/opsx:ff` if you want to move fast.
|
||||
```
|
||||
|
||||
Exit gracefully.
|
||||
|
||||
---
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Follow the EXPLAIN → DO → SHOW → PAUSE pattern** at key transitions (after explore, after proposal draft, after tasks, after archive)
|
||||
- **Keep narration light** during implementation—teach without lecturing
|
||||
- **Don't skip phases** even if the change is small—the goal is teaching the workflow
|
||||
- **Pause for acknowledgment** at marked points, but don't over-pause
|
||||
- **Handle exits gracefully**—never pressure the user to continue
|
||||
- **Use real codebase tasks**—don't simulate or use fake examples
|
||||
- **Adjust scope gently**—guide toward smaller tasks but respect user choice
|
||||
@@ -1,132 +0,0 @@
|
||||
---
|
||||
description: Sync delta specs from a change to main specs
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Sync delta specs from a change to main specs.
|
||||
|
||||
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
||||
|
||||
**Input**: Optionally specify a change name after `/opsx:sync` (e.g., `/opsx:sync add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
|
||||
Show changes that have delta specs (under `specs/` directory).
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Find delta specs**
|
||||
|
||||
Look for delta spec files in `openspec/changes/<name>/specs/*/spec.md`.
|
||||
|
||||
Each delta spec file contains sections like:
|
||||
- `## ADDED Requirements` - New requirements to add
|
||||
- `## MODIFIED Requirements` - Changes to existing requirements
|
||||
- `## REMOVED Requirements` - Requirements to remove
|
||||
- `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)
|
||||
|
||||
If no delta specs found, inform user and stop.
|
||||
|
||||
3. **For each delta spec, apply changes to main specs**
|
||||
|
||||
For each capability with a delta spec at `openspec/changes/<name>/specs/<capability>/spec.md`:
|
||||
|
||||
a. **Read the delta spec** to understand the intended changes
|
||||
|
||||
b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)
|
||||
|
||||
c. **Apply changes intelligently**:
|
||||
|
||||
**ADDED Requirements:**
|
||||
- If requirement doesn't exist in main spec → add it
|
||||
- If requirement already exists → update it to match (treat as implicit MODIFIED)
|
||||
|
||||
**MODIFIED Requirements:**
|
||||
- Find the requirement in main spec
|
||||
- Apply the changes - this can be:
|
||||
- Adding new scenarios (don't need to copy existing ones)
|
||||
- Modifying existing scenarios
|
||||
- Changing the requirement description
|
||||
- Preserve scenarios/content not mentioned in the delta
|
||||
|
||||
**REMOVED Requirements:**
|
||||
- Remove the entire requirement block from main spec
|
||||
|
||||
**RENAMED Requirements:**
|
||||
- Find the FROM requirement, rename to TO
|
||||
|
||||
d. **Create new main spec** if capability doesn't exist yet:
|
||||
- Create `openspec/specs/<capability>/spec.md`
|
||||
- Add Purpose section (can be brief, mark as TBD)
|
||||
- Add Requirements section with the ADDED requirements
|
||||
|
||||
4. **Show summary**
|
||||
|
||||
After applying all changes, summarize:
|
||||
- Which capabilities were updated
|
||||
- What changes were made (requirements added/modified/removed/renamed)
|
||||
|
||||
**Delta Spec Format Reference**
|
||||
|
||||
```markdown
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: New Feature
|
||||
The system SHALL do something new.
|
||||
|
||||
#### Scenario: Basic case
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Existing Feature
|
||||
#### Scenario: New scenario to add
|
||||
- **WHEN** user does A
|
||||
- **THEN** system does B
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: Deprecated Feature
|
||||
|
||||
## RENAMED Requirements
|
||||
|
||||
- FROM: `### Requirement: Old Name`
|
||||
- TO: `### Requirement: New Name`
|
||||
```
|
||||
|
||||
**Key Principle: Intelligent Merging**
|
||||
|
||||
Unlike programmatic merging, you can apply **partial updates**:
|
||||
- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios
|
||||
- The delta represents *intent*, not a wholesale replacement
|
||||
- Use your judgment to merge changes sensibly
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
## Specs Synced: <change-name>
|
||||
|
||||
Updated main specs:
|
||||
|
||||
**<capability-1>**:
|
||||
- Added requirement: "New Feature"
|
||||
- Modified requirement: "Existing Feature" (added 1 scenario)
|
||||
|
||||
**<capability-2>**:
|
||||
- Created new spec file
|
||||
- Added requirement: "Another Feature"
|
||||
|
||||
Main specs are now updated. The change remains active - archive when implementation is complete.
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Read both delta and main specs before making changes
|
||||
- Preserve existing content not mentioned in delta
|
||||
- If something is unclear, ask for clarification
|
||||
- Show what you're changing as you go
|
||||
- The operation should be idempotent - running twice should give same result
|
||||
@@ -1,162 +0,0 @@
|
||||
---
|
||||
description: Verify implementation matches change artifacts before archiving
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
Verify that an implementation matches the change artifacts (specs, tasks, design).
|
||||
|
||||
**Input**: Optionally specify a change name after `/opsx:verify` (e.g., `/opsx:verify add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
|
||||
Show changes that have implementation tasks (tasks artifact exists).
|
||||
Include the schema used for each change if available.
|
||||
Mark changes with incomplete tasks as "(In Progress)".
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Check status to understand the schema**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
||||
- Which artifacts exist for this change
|
||||
|
||||
3. **Get the change directory and load artifacts**
|
||||
|
||||
```bash
|
||||
openspec instructions apply --change "<name>" --json
|
||||
```
|
||||
|
||||
This returns the change directory and context files. Read all available artifacts from `contextFiles`.
|
||||
|
||||
4. **Initialize verification report structure**
|
||||
|
||||
Create a report structure with three dimensions:
|
||||
- **Completeness**: Track tasks and spec coverage
|
||||
- **Correctness**: Track requirement implementation and scenario coverage
|
||||
- **Coherence**: Track design adherence and pattern consistency
|
||||
|
||||
Each dimension can have CRITICAL, WARNING, or SUGGESTION issues.
|
||||
|
||||
5. **Verify Completeness**
|
||||
|
||||
**Task Completion**:
|
||||
- If tasks.md exists in contextFiles, read it
|
||||
- Parse checkboxes: `- [ ]` (incomplete) vs `- [x]` (complete)
|
||||
- Count complete vs total tasks
|
||||
- If incomplete tasks exist:
|
||||
- Add CRITICAL issue for each incomplete task
|
||||
- Recommendation: "Complete task: <description>" or "Mark as done if already implemented"
|
||||
|
||||
**Spec Coverage**:
|
||||
- If delta specs exist in `openspec/changes/<name>/specs/`:
|
||||
- Extract all requirements (marked with "### Requirement:")
|
||||
- For each requirement:
|
||||
- Search codebase for keywords related to the requirement
|
||||
- Assess if implementation likely exists
|
||||
- If requirements appear unimplemented:
|
||||
- Add CRITICAL issue: "Requirement not found: <requirement name>"
|
||||
- Recommendation: "Implement requirement X: <description>"
|
||||
|
||||
6. **Verify Correctness**
|
||||
|
||||
**Requirement Implementation Mapping**:
|
||||
- For each requirement from delta specs:
|
||||
- Search codebase for implementation evidence
|
||||
- If found, note file paths and line ranges
|
||||
- Assess if implementation matches requirement intent
|
||||
- If divergence detected:
|
||||
- Add WARNING: "Implementation may diverge from spec: <details>"
|
||||
- Recommendation: "Review <file>:<lines> against requirement X"
|
||||
|
||||
**Scenario Coverage**:
|
||||
- For each scenario in delta specs (marked with "#### Scenario:"):
|
||||
- Check if conditions are handled in code
|
||||
- Check if tests exist covering the scenario
|
||||
- If scenario appears uncovered:
|
||||
- Add WARNING: "Scenario not covered: <scenario name>"
|
||||
- Recommendation: "Add test or implementation for scenario: <description>"
|
||||
|
||||
7. **Verify Coherence**
|
||||
|
||||
**Design Adherence**:
|
||||
- If design.md exists in contextFiles:
|
||||
- Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:")
|
||||
- Verify implementation follows those decisions
|
||||
- If contradiction detected:
|
||||
- Add WARNING: "Design decision not followed: <decision>"
|
||||
- Recommendation: "Update implementation or revise design.md to match reality"
|
||||
- If no design.md: Skip design adherence check, note "No design.md to verify against"
|
||||
|
||||
**Code Pattern Consistency**:
|
||||
- Review new code for consistency with project patterns
|
||||
- Check file naming, directory structure, coding style
|
||||
- If significant deviations found:
|
||||
- Add SUGGESTION: "Code pattern deviation: <details>"
|
||||
- Recommendation: "Consider following project pattern: <example>"
|
||||
|
||||
8. **Generate Verification Report**
|
||||
|
||||
**Summary Scorecard**:
|
||||
```
|
||||
## Verification Report: <change-name>
|
||||
|
||||
### Summary
|
||||
| Dimension | Status |
|
||||
|--------------|------------------|
|
||||
| Completeness | X/Y tasks, N reqs|
|
||||
| Correctness | M/N reqs covered |
|
||||
| Coherence | Followed/Issues |
|
||||
```
|
||||
|
||||
**Issues by Priority**:
|
||||
|
||||
1. **CRITICAL** (Must fix before archive):
|
||||
- Incomplete tasks
|
||||
- Missing requirement implementations
|
||||
- Each with specific, actionable recommendation
|
||||
|
||||
2. **WARNING** (Should fix):
|
||||
- Spec/design divergences
|
||||
- Missing scenario coverage
|
||||
- Each with specific recommendation
|
||||
|
||||
3. **SUGGESTION** (Nice to fix):
|
||||
- Pattern inconsistencies
|
||||
- Minor improvements
|
||||
- Each with specific recommendation
|
||||
|
||||
**Final Assessment**:
|
||||
- If CRITICAL issues: "X critical issue(s) found. Fix before archiving."
|
||||
- If only warnings: "No critical issues. Y warning(s) to consider. Ready for archive (with noted improvements)."
|
||||
- If all clear: "All checks passed. Ready for archive."
|
||||
|
||||
**Verification Heuristics**
|
||||
|
||||
- **Completeness**: Focus on objective checklist items (checkboxes, requirements list)
|
||||
- **Correctness**: Use keyword search, file path analysis, reasonable inference - don't require perfect certainty
|
||||
- **Coherence**: Look for glaring inconsistencies, don't nitpick style
|
||||
- **False Positives**: When uncertain, prefer SUGGESTION over WARNING, WARNING over CRITICAL
|
||||
- **Actionability**: Every issue must have a specific recommendation with file/line references where applicable
|
||||
|
||||
**Graceful Degradation**
|
||||
|
||||
- If only tasks.md exists: verify task completion only, skip spec/design checks
|
||||
- If tasks + specs exist: verify completeness and correctness, skip design
|
||||
- If full artifacts: verify all three dimensions
|
||||
- Always note which checks were skipped and why
|
||||
|
||||
**Output Format**
|
||||
|
||||
Use clear markdown with:
|
||||
- Table for summary scorecard
|
||||
- Grouped lists for issues (CRITICAL/WARNING/SUGGESTION)
|
||||
- Code references in format: `file.ts:123`
|
||||
- Specific, actionable recommendations
|
||||
- No vague suggestions like "consider reviewing"
|
||||
@@ -1,198 +0,0 @@
|
||||
---
|
||||
name: export-datasource
|
||||
description: Project-specific guide for implementing, modifying, or reviewing export data sources in junhong_cmp_fiber. Use when Codex needs to add a new export scene, extend filters or dynamic columns, register an export scene, change export task query behavior, or explain/debug the DataSource-based export system.
|
||||
---
|
||||
|
||||
# Export Datasource
|
||||
|
||||
Use this skill to work on this project's DataSource-based export system. It covers developer-facing export scene implementation, not one-off manual export operations.
|
||||
|
||||
## First Reads
|
||||
|
||||
Before editing export code, read the relevant current files:
|
||||
|
||||
- `internal/exporter/datasource.go`
|
||||
- `internal/exporter/query_params.go`
|
||||
- `internal/exporter/filter_helpers.go`
|
||||
- `internal/exporter/registry.go`
|
||||
- Existing scene closest to the new scene:
|
||||
- `internal/exporter/device_scene.go`
|
||||
- `internal/exporter/iot_card_scene.go`
|
||||
- For request/scene validation:
|
||||
- `internal/model/dto/export_task_dto.go`
|
||||
- `pkg/constants/constants.go`
|
||||
- For behavior across worker stages:
|
||||
- `internal/task/export_dispatch.go`
|
||||
- `internal/task/export_shard.go`
|
||||
- `internal/task/export_finalize.go`
|
||||
|
||||
Read `references/export-scene-template.md` when adding a new scene or when a concrete code skeleton is useful.
|
||||
|
||||
## Mental Model
|
||||
|
||||
The framework owns async execution, sharding, file generation, OSS upload, and download URLs. A scene implementation owns only data semantics:
|
||||
|
||||
```go
|
||||
type DataSource interface {
|
||||
Scene() string
|
||||
Count(ctx context.Context, params ExportParams) (int, error)
|
||||
Headers(ctx context.Context, params ExportParams) ([]string, error)
|
||||
Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error)
|
||||
}
|
||||
```
|
||||
|
||||
Execution flow:
|
||||
|
||||
1. Admin API creates `tb_export_task` and enqueues `export:dispatch`.
|
||||
2. Dispatch parses `query_json.filters` plus permission snapshot into `ExportParams`.
|
||||
3. Dispatch calls `Headers` once and stores `query_json.resolved_headers`.
|
||||
4. Dispatch calls `Count` and creates `tb_export_shard_task` rows with `shard_offset` and `shard_limit`.
|
||||
5. Shard calls `Fetch`, writes headerless CSV shard files, and uploads them.
|
||||
6. Finalize downloads shard CSV files in shard order, writes one header row, uploads final CSV or converts CSV to XLSX.
|
||||
|
||||
Do not reintroduce keyset cursor logic. New scenes must use offset/limit through `Fetch`.
|
||||
|
||||
## Implementation Workflow
|
||||
|
||||
1. Add a scene constant in `pkg/constants/constants.go`.
|
||||
2. Update `internal/model/dto/export_task_dto.go` validation and descriptions for `scene`.
|
||||
3. Implement `internal/exporter/<scene>_scene.go` with `DataSource`.
|
||||
4. Register the source in `NewDefaultRegistry`.
|
||||
5. Update `IsSupportedScene` if it is used by the current code path.
|
||||
6. Add or update migrations only if the exported domain needs schema/index changes. Do not change export task tables unless the framework contract changes.
|
||||
7. Build and manually verify. This repository forbids automated tests unless the user explicitly requests them.
|
||||
|
||||
## DataSource Rules
|
||||
|
||||
- `Scene` must return the constant, not a string literal.
|
||||
- `Count` and `Fetch` must apply the same filters and permission scope.
|
||||
- `Headers` defines the exact column contract for the whole task. Dispatch stores it once; shards and finalize reuse it.
|
||||
- `Fetch` must return rows aligned to `Headers`; the framework pads/truncates as a fallback, but the source should be correct.
|
||||
- `Fetch` must use stable ordering, normally `ORDER BY id ASC`.
|
||||
- Return string values only. Format time as `2006-01-02 15:04:05` unless the surrounding scene establishes another convention.
|
||||
- Use GORM only. Do not use `database/sql`.
|
||||
- Keep SQL parameters bound through GORM placeholders. Do not concatenate user-controlled values into SQL.
|
||||
- Keep comments, logs, errors, and documentation in Chinese per project rules.
|
||||
|
||||
## Filters And Permissions
|
||||
|
||||
Incoming task query shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"filters": {
|
||||
"status": 1,
|
||||
"shop_id": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`ParseExportParams` exposes:
|
||||
|
||||
- `Filters`: `query_json.filters`
|
||||
- `ScopeShopIDs`: shop permission snapshot captured at task creation
|
||||
- `UserType`: creator user type snapshot
|
||||
|
||||
Apply permission scope inside each DataSource:
|
||||
|
||||
```go
|
||||
query = applyExportShopScope(query, params, "shop_id")
|
||||
```
|
||||
|
||||
For aliased queries:
|
||||
|
||||
```go
|
||||
query = applyExportShopScope(query, params, "o.shop_id")
|
||||
```
|
||||
|
||||
Use helpers from `filter_helpers.go`:
|
||||
|
||||
- `filterInt`
|
||||
- `filterUint`
|
||||
- `filterString`
|
||||
- `filterBool`
|
||||
- `filterTime`
|
||||
- `formatOptionalUint`
|
||||
- `formatOptionalTime`
|
||||
|
||||
Do not read live permissions from context inside a DataSource. Export tasks must use the permission snapshot stored at creation time.
|
||||
|
||||
## Dynamic Columns
|
||||
|
||||
Use dynamic headers only when the task parameters or data require it. If `Headers` changes based on filters, `Fetch` must produce the same shape for every shard under the same `ExportParams`.
|
||||
|
||||
Example pattern:
|
||||
|
||||
```go
|
||||
headers := []string{"ID", "ICCID", "状态"}
|
||||
if filterBool(params.Filters, "with_package") {
|
||||
headers = append(headers, "套餐名称", "套餐状态")
|
||||
}
|
||||
return headers, nil
|
||||
```
|
||||
|
||||
## Join Queries
|
||||
|
||||
JOIN-based exports are allowed. Keep these constraints:
|
||||
|
||||
- Preserve one output row per intended exported entity unless the scene explicitly exports detail rows.
|
||||
- If a JOIN can multiply rows, make `Count` match the exported row semantics exactly.
|
||||
- Use table aliases consistently in filters and scope columns.
|
||||
- Prefer explicit `Select` into a local row struct for multi-table exports.
|
||||
- Keep `Order`, `Limit`, and `Offset` on the final query used by `Fetch`.
|
||||
|
||||
## User-Facing API Notes
|
||||
|
||||
Current API group:
|
||||
|
||||
- `POST /api/admin/export-tasks`
|
||||
- `GET /api/admin/export-tasks`
|
||||
- `GET /api/admin/export-tasks/:id`
|
||||
- `POST /api/admin/export-tasks/:id/cancel`
|
||||
|
||||
Creation request:
|
||||
|
||||
```json
|
||||
{
|
||||
"scene": "iot_card",
|
||||
"format": "csv",
|
||||
"query": {
|
||||
"filters": {
|
||||
"shop_id": 1,
|
||||
"with_package": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Formats are `csv` and `xlsx`. Final download URLs are returned from task detail after completion.
|
||||
|
||||
## Verification
|
||||
|
||||
This project forbids automated tests and `_test.go` files unless the user explicitly asks for tests. Use manual verification:
|
||||
|
||||
```bash
|
||||
go build ./internal/exporter/...
|
||||
go build ./internal/task/...
|
||||
go build ./...
|
||||
```
|
||||
|
||||
Then create an export task through the API and inspect:
|
||||
|
||||
- `tb_export_task.query_json` contains original `filters` and generated `resolved_headers`.
|
||||
- `tb_export_task.total_rows` matches the filtered query.
|
||||
- `tb_export_shard_task.shard_offset` and `shard_limit` are filled.
|
||||
- Shards reach success and final task reaches completed status.
|
||||
- Downloaded file has one header row, expected row count, correct filtering, and valid CSV/XLSX format.
|
||||
|
||||
Use PostgreSQL MCP/manual SQL for data validation when needed.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- Updating only `Fetch` and forgetting `Count`, causing wrong shard planning.
|
||||
- Returning dynamic rows whose column count does not match `Headers`.
|
||||
- Filtering by requested `shop_id` without also applying `ScopeShopIDs`.
|
||||
- Using context/user middleware inside worker DataSource logic.
|
||||
- Registering the source but forgetting DTO `oneof`, so API rejects the new scene.
|
||||
- Writing XLSX shard files. Shards should be CSV; finalize handles final format.
|
||||
- Adding automated tests despite the repository ban.
|
||||
@@ -1,4 +0,0 @@
|
||||
interface:
|
||||
display_name: "导出数据源开发"
|
||||
short_description: "指导新增和维护项目导出数据源场景"
|
||||
default_prompt: "Use $export-datasource to add a new export scene for this project."
|
||||
@@ -1,222 +0,0 @@
|
||||
# Export Scene Template
|
||||
|
||||
Use this reference when adding a new export scene.
|
||||
|
||||
## Minimal Checklist
|
||||
|
||||
- Add `ExportTaskSceneXxx` in `pkg/constants/constants.go`.
|
||||
- Add the scene to `CreateExportTaskRequest.Scene` and `ListExportTaskRequest.Scene` validation/description.
|
||||
- Create `internal/exporter/<scene>_scene.go`.
|
||||
- Register `NewXxxDataSource(db)` in `internal/exporter/registry.go`.
|
||||
- Update `IsSupportedScene`.
|
||||
- Run `gofmt` on changed Go files.
|
||||
- Run `go build ./internal/exporter/...`, `go build ./internal/task/...`, and `go build ./...`.
|
||||
- Manually create a task and validate database rows plus downloaded file.
|
||||
|
||||
## Skeleton
|
||||
|
||||
```go
|
||||
package exporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// XxxDataSource Xxx 导出数据源。
|
||||
type XxxDataSource struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewXxxDataSource 创建 Xxx 导出数据源。
|
||||
func NewXxxDataSource(db *gorm.DB) *XxxDataSource {
|
||||
return &XxxDataSource{db: db}
|
||||
}
|
||||
|
||||
// Scene 返回导出场景编码。
|
||||
func (s *XxxDataSource) Scene() string {
|
||||
return constants.ExportTaskSceneXxx
|
||||
}
|
||||
|
||||
// Count 统计 Xxx 导出行数。
|
||||
func (s *XxxDataSource) Count(ctx context.Context, params ExportParams) (int, error) {
|
||||
var total int64
|
||||
query := s.applyFilters(s.db.WithContext(ctx).Model(&model.Xxx{}), params)
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(total), nil
|
||||
}
|
||||
|
||||
// Headers 返回 Xxx 导出表头。
|
||||
func (s *XxxDataSource) Headers(ctx context.Context, params ExportParams) ([]string, error) {
|
||||
return []string{"ID", "名称", "状态", "店铺ID", "创建时间"}, nil
|
||||
}
|
||||
|
||||
// Fetch 按 offset/limit 查询 Xxx 导出数据。
|
||||
func (s *XxxDataSource) Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error) {
|
||||
if limit <= 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
var items []model.Xxx
|
||||
query := s.applyFilters(s.db.WithContext(ctx).Model(&model.Xxx{}), params).
|
||||
Order("id ASC").
|
||||
Limit(limit).
|
||||
Offset(offset)
|
||||
if err := query.Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([][]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
rows = append(rows, []string{
|
||||
strconv.FormatUint(uint64(item.ID), 10),
|
||||
item.Name,
|
||||
strconv.Itoa(item.Status),
|
||||
formatOptionalUint(item.ShopID),
|
||||
item.CreatedAt.Format(exportTimeLayout),
|
||||
})
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (s *XxxDataSource) applyFilters(query *gorm.DB, params ExportParams) *gorm.DB {
|
||||
query = applyExportShopScope(query, params, "shop_id")
|
||||
|
||||
if status, ok := filterInt(params.Filters, "status"); ok {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
if shopID, ok := filterUint(params.Filters, "shop_id"); ok {
|
||||
query = query.Where("shop_id = ?", shopID)
|
||||
}
|
||||
if start, ok := filterTime(params.Filters, "created_at_start"); ok {
|
||||
query = query.Where("created_at >= ?", start)
|
||||
}
|
||||
if end, ok := filterTime(params.Filters, "created_at_end"); ok {
|
||||
query = query.Where("created_at <= ?", end)
|
||||
}
|
||||
return query
|
||||
}
|
||||
```
|
||||
|
||||
## JOIN Skeleton
|
||||
|
||||
Use this shape for multi-table exports:
|
||||
|
||||
```go
|
||||
type xxxExportRow struct {
|
||||
ID uint
|
||||
Name string
|
||||
Status int
|
||||
ShopID *uint
|
||||
ExtraName string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func (s *XxxDataSource) Fetch(ctx context.Context, params ExportParams, offset, limit int) ([][]string, error) {
|
||||
if limit <= 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
var items []xxxExportRow
|
||||
query := s.applyFilters(s.db.WithContext(ctx).Table("tb_xxx AS x"), params, "x.").
|
||||
Select(`
|
||||
x.id,
|
||||
x.name,
|
||||
x.status,
|
||||
x.shop_id,
|
||||
COALESCE(e.name, '') AS extra_name,
|
||||
x.created_at
|
||||
`).
|
||||
Joins("LEFT JOIN tb_extra AS e ON e.xxx_id = x.id AND e.deleted_at IS NULL").
|
||||
Where("x.deleted_at IS NULL").
|
||||
Order("x.id ASC").
|
||||
Limit(limit).
|
||||
Offset(offset)
|
||||
if err := query.Scan(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([][]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
rows = append(rows, []string{
|
||||
strconv.FormatUint(uint64(item.ID), 10),
|
||||
item.Name,
|
||||
strconv.Itoa(item.Status),
|
||||
formatOptionalUint(item.ShopID),
|
||||
item.ExtraName,
|
||||
item.CreatedAt.Format(exportTimeLayout),
|
||||
})
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (s *XxxDataSource) applyFilters(query *gorm.DB, params ExportParams, prefix string) *gorm.DB {
|
||||
query = applyExportShopScope(query, params, prefix+"shop_id")
|
||||
if status, ok := filterInt(params.Filters, "status"); ok {
|
||||
query = query.Where(prefix+"status = ?", status)
|
||||
}
|
||||
return query
|
||||
}
|
||||
```
|
||||
|
||||
If the JOIN multiplies rows, update `Count` to count the same exported row set. Do not count only the base table unless `Fetch` also returns one row per base record.
|
||||
|
||||
## Registry Patch
|
||||
|
||||
```go
|
||||
func NewDefaultRegistry(db *gorm.DB) *Registry {
|
||||
return NewRegistry(
|
||||
NewDeviceDataSource(db),
|
||||
NewIotCardDataSource(db),
|
||||
NewXxxDataSource(db),
|
||||
)
|
||||
}
|
||||
|
||||
func IsSupportedScene(scene string) bool {
|
||||
switch scene {
|
||||
case constants.ExportTaskSceneDevice,
|
||||
constants.ExportTaskSceneIotCard,
|
||||
constants.ExportTaskSceneXxx:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Manual API Smoke
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://localhost:端口/api/admin/export-tasks' \
|
||||
-H 'Authorization: Bearer <token>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"scene": "xxx",
|
||||
"format": "csv",
|
||||
"query": {
|
||||
"filters": {
|
||||
"status": 1
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Check database:
|
||||
|
||||
```sql
|
||||
SELECT id, scene, format, status, total_rows, total_shards, query_json
|
||||
FROM tb_export_task
|
||||
WHERE id = <task_id>;
|
||||
|
||||
SELECT shard_no, status, shard_offset, shard_limit, row_count, file_key
|
||||
FROM tb_export_shard_task
|
||||
WHERE task_id = <task_id>
|
||||
ORDER BY shard_no;
|
||||
```
|
||||
@@ -1,118 +0,0 @@
|
||||
---
|
||||
name: openspec-archive-change
|
||||
description: Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
---
|
||||
|
||||
Archive a completed change in the experimental workflow.
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
|
||||
Show only active changes (not already archived).
|
||||
Include the schema used for each change if available.
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Check artifact completion status**
|
||||
|
||||
Run `openspec status --change "<name>" --json` to check artifact completion.
|
||||
|
||||
Parse the JSON to understand:
|
||||
- `schemaName`: The workflow being used
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context
|
||||
- `artifacts`: List of artifacts with their status (`done` or other)
|
||||
|
||||
**If any artifacts are not `done`:**
|
||||
- Display warning listing incomplete artifacts
|
||||
- Use **AskUserQuestion tool** to confirm user wants to proceed
|
||||
- Proceed if user confirms
|
||||
|
||||
3. **Check task completion status**
|
||||
|
||||
Read the tasks file (typically `tasks.md`) to check for incomplete tasks.
|
||||
|
||||
Count tasks marked with `- [ ]` (incomplete) vs `- [x]` (complete).
|
||||
|
||||
**If incomplete tasks found:**
|
||||
- Display warning showing count of incomplete tasks
|
||||
- Use **AskUserQuestion tool** to confirm user wants to proceed
|
||||
- Proceed if user confirms
|
||||
|
||||
**If no tasks file exists:** Proceed without task-related warning.
|
||||
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
Use `artifactPaths.specs.existingOutputPaths` from status JSON to check for delta specs. If none exist, proceed without sync prompt.
|
||||
|
||||
**If delta specs exist:**
|
||||
- Compare each delta spec with its corresponding main spec at `openspec/specs/<capability>/spec.md`
|
||||
- Determine what changes would be applied (adds, modifications, removals, renames)
|
||||
- Show a combined summary before prompting
|
||||
|
||||
**Prompt options:**
|
||||
- If changes needed: "Sync now (recommended)", "Archive without syncing"
|
||||
- If already synced: "Archive now", "Sync anyway", "Cancel"
|
||||
|
||||
If user chooses sync, use Task tool (subagent_type: "general-purpose", prompt: "Use Skill tool to invoke openspec-sync-specs for change '<name>'. Delta spec analysis: <include the analyzed delta spec summary>"). Proceed to archive regardless of choice.
|
||||
|
||||
5. **Perform the archive**
|
||||
|
||||
Create an `archive` directory under `planningHome.changesDir` if it doesn't exist:
|
||||
```bash
|
||||
mkdir -p "<planningHome.changesDir>/archive"
|
||||
```
|
||||
|
||||
Generate target name using current date: `YYYY-MM-DD-<change-name>`
|
||||
|
||||
**Check if target already exists:**
|
||||
- If yes: Fail with error, suggest renaming existing archive or using different date
|
||||
- If no: Move `changeRoot` to the archive directory
|
||||
|
||||
```bash
|
||||
mv "<changeRoot>" "<planningHome.changesDir>/archive/YYYY-MM-DD-<name>"
|
||||
```
|
||||
|
||||
6. **Display summary**
|
||||
|
||||
Show archive completion summary including:
|
||||
- Change name
|
||||
- Schema that was used
|
||||
- Archive location
|
||||
- Whether specs were synced (if applicable)
|
||||
- Note about any warnings (incomplete artifacts/tasks)
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
## Archive Complete
|
||||
|
||||
**Change:** <change-name>
|
||||
**Schema:** <schema-name>
|
||||
**Archived to:** the archive path derived from `planningHome.changesDir`/YYYY-MM-DD-<name>/
|
||||
**Specs:** ✓ Synced to main specs (or "No delta specs" or "Sync skipped")
|
||||
|
||||
All artifacts complete. All tasks complete.
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Always prompt for change selection if not provided
|
||||
- Use artifact graph (openspec status --json) for completion checking
|
||||
- Don't block archive on warnings - just inform and confirm
|
||||
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
|
||||
- Show clear summary of what happened
|
||||
- If sync is requested, use openspec-sync-specs approach (agent-driven)
|
||||
- If delta specs exist, always run the sync assessment and show the combined summary before prompting
|
||||
@@ -1,114 +0,0 @@
|
||||
---
|
||||
name: openspec-propose
|
||||
description: Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
---
|
||||
|
||||
Propose a new change - create the change and generate all artifacts in one step.
|
||||
|
||||
I'll create a change with artifacts:
|
||||
- proposal.md (what & why)
|
||||
- design.md (how)
|
||||
- tasks.md (implementation steps)
|
||||
|
||||
When ready to implement, run /opsx:apply
|
||||
|
||||
---
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no clear input provided, ask what they want to build**
|
||||
|
||||
Use the **AskUserQuestion tool** (open-ended, no preset options) to ask:
|
||||
> "What change do you want to work on? Describe what you want to build or fix."
|
||||
|
||||
From their description, derive a kebab-case name (e.g., "add user authentication" → `add-user-auth`).
|
||||
|
||||
**IMPORTANT**: Do NOT proceed without understanding what the user wants to build.
|
||||
|
||||
2. **Create the change directory**
|
||||
```bash
|
||||
openspec new change "<name>"
|
||||
```
|
||||
This creates a scaffolded change in the planning home resolved by the CLI with `.openspec.yaml`.
|
||||
|
||||
3. **Get the artifact build order**
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
Parse the JSON to get:
|
||||
- `applyRequires`: array of artifact IDs needed before implementation (e.g., `["tasks"]`)
|
||||
- `artifacts`: list of all artifacts with their status and dependencies
|
||||
- `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`: path and scope context. Use these instead of assuming repo-local paths.
|
||||
|
||||
4. **Create artifacts in sequence until apply-ready**
|
||||
|
||||
Use the **TodoWrite tool** to track progress through the artifacts.
|
||||
|
||||
Loop through artifacts in dependency order (artifacts with no pending dependencies first):
|
||||
|
||||
a. **For each artifact that is `ready` (dependencies satisfied)**:
|
||||
- Get instructions:
|
||||
```bash
|
||||
openspec instructions <artifact-id> --change "<name>" --json
|
||||
```
|
||||
- The instructions JSON includes:
|
||||
- `context`: Project background (constraints for you - do NOT include in output)
|
||||
- `rules`: Artifact-specific rules (constraints for you - do NOT include in output)
|
||||
- `template`: The structure to use for your output file
|
||||
- `instruction`: Schema-specific guidance for this artifact type
|
||||
- `resolvedOutputPath`: Resolved path or pattern to write the artifact
|
||||
- `dependencies`: Completed artifacts to read for context
|
||||
- Read any completed dependency files for context
|
||||
- Create the artifact file using `template` as the structure and write it to `resolvedOutputPath`
|
||||
- Apply `context` and `rules` as constraints - but do NOT copy them into the file
|
||||
- Show brief progress: "Created <artifact-id>"
|
||||
|
||||
b. **Continue until all `applyRequires` artifacts are complete**
|
||||
- After creating each artifact, re-run `openspec status --change "<name>" --json`
|
||||
- Check if every artifact ID in `applyRequires` has `status: "done"` in the artifacts array
|
||||
- Stop when all `applyRequires` artifacts are done
|
||||
|
||||
c. **If an artifact requires user input** (unclear context):
|
||||
- Use **AskUserQuestion tool** to clarify
|
||||
- Then continue with creation
|
||||
|
||||
5. **Show final status**
|
||||
```bash
|
||||
openspec status --change "<name>"
|
||||
```
|
||||
|
||||
**Output**
|
||||
|
||||
After completing all artifacts, summarize:
|
||||
- Change name and location
|
||||
- List of artifacts created with brief descriptions
|
||||
- What's ready: "All artifacts created! Ready for implementation."
|
||||
- Prompt: "Run `/opsx:apply` or ask me to implement to start working on the tasks."
|
||||
|
||||
**Artifact Creation Guidelines**
|
||||
|
||||
- Follow the `instruction` field from `openspec instructions` for each artifact type
|
||||
- The schema defines what each artifact should contain - follow it
|
||||
- Read dependency artifacts for context before creating new ones
|
||||
- Use `template` as the structure for your output file - fill in its sections
|
||||
- **IMPORTANT**: `context` and `rules` are constraints for YOU, not content for the file
|
||||
- Do NOT copy `<context>`, `<rules>`, `<project_context>` blocks into the artifact
|
||||
- These guide what you write, but should never appear in the output
|
||||
|
||||
**Guardrails**
|
||||
- Create ALL artifacts needed for implementation (as defined by schema's `apply.requires`)
|
||||
- Always read dependency artifacts before creating a new one
|
||||
- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum
|
||||
- If a change with that name already exists, ask if user wants to continue it or create a new one
|
||||
- Verify each artifact file exists after writing before proceeding to next
|
||||
@@ -1,148 +0,0 @@
|
||||
---
|
||||
name: openspec-sync-specs
|
||||
description: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.
|
||||
allowed-tools: Bash(openspec:*)
|
||||
license: MIT
|
||||
compatibility: Requires openspec CLI.
|
||||
metadata:
|
||||
author: openspec
|
||||
version: "1.0"
|
||||
generatedBy: "1.6.0"
|
||||
---
|
||||
|
||||
Sync delta specs from a change to main specs.
|
||||
|
||||
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
||||
|
||||
**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.
|
||||
|
||||
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
||||
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
||||
|
||||
Show changes that have delta specs (under `specs/` directory).
|
||||
|
||||
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
||||
|
||||
2. **Resolve change context**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
openspec status --change "<name>" --json
|
||||
```
|
||||
|
||||
3. **Find delta specs**
|
||||
|
||||
Use `artifactPaths.specs.existingOutputPaths` from the status JSON as the list of delta spec files.
|
||||
|
||||
Each delta spec file contains sections like:
|
||||
- `## ADDED Requirements` - New requirements to add
|
||||
- `## MODIFIED Requirements` - Changes to existing requirements
|
||||
- `## REMOVED Requirements` - Requirements to remove
|
||||
- `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)
|
||||
|
||||
If no delta specs found, inform user and stop.
|
||||
|
||||
4. **For each delta spec, apply changes to main specs**
|
||||
|
||||
For each repo-local capability delta spec path returned by the CLI:
|
||||
|
||||
a. **Read the delta spec** to understand the intended changes
|
||||
|
||||
b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)
|
||||
|
||||
c. **Apply changes intelligently**:
|
||||
|
||||
**ADDED Requirements:**
|
||||
- If requirement doesn't exist in main spec → add it
|
||||
- If requirement already exists → update it to match (treat as implicit MODIFIED)
|
||||
|
||||
**MODIFIED Requirements:**
|
||||
- Find the requirement in main spec
|
||||
- Apply the changes - this can be:
|
||||
- Adding new scenarios (don't need to copy existing ones)
|
||||
- Modifying existing scenarios
|
||||
- Changing the requirement description
|
||||
- Preserve scenarios/content not mentioned in the delta
|
||||
|
||||
**REMOVED Requirements:**
|
||||
- Remove the entire requirement block from main spec
|
||||
|
||||
**RENAMED Requirements:**
|
||||
- Find the FROM requirement, rename to TO
|
||||
|
||||
d. **Create new main spec** if capability doesn't exist yet:
|
||||
- Create `openspec/specs/<capability>/spec.md`
|
||||
- Add Purpose section (can be brief, mark as TBD)
|
||||
- Add Requirements section with the ADDED requirements
|
||||
|
||||
5. **Show summary**
|
||||
|
||||
After applying all changes, summarize:
|
||||
- Which capabilities were updated
|
||||
- What changes were made (requirements added/modified/removed/renamed)
|
||||
|
||||
**Delta Spec Format Reference**
|
||||
|
||||
```markdown
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: New Feature
|
||||
The system SHALL do something new.
|
||||
|
||||
#### Scenario: Basic case
|
||||
- **WHEN** user does X
|
||||
- **THEN** system does Y
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Existing Feature
|
||||
#### Scenario: New scenario to add
|
||||
- **WHEN** user does A
|
||||
- **THEN** system does B
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: Deprecated Feature
|
||||
|
||||
## RENAMED Requirements
|
||||
|
||||
- FROM: `### Requirement: Old Name`
|
||||
- TO: `### Requirement: New Name`
|
||||
```
|
||||
|
||||
**Key Principle: Intelligent Merging**
|
||||
|
||||
Unlike programmatic merging, you can apply **partial updates**:
|
||||
- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios
|
||||
- The delta represents *intent*, not a wholesale replacement
|
||||
- Use your judgment to merge changes sensibly
|
||||
|
||||
**Output On Success**
|
||||
|
||||
```
|
||||
## Specs Synced: <change-name>
|
||||
|
||||
Updated main specs:
|
||||
|
||||
**<capability-1>**:
|
||||
- Added requirement: "New Feature"
|
||||
- Modified requirement: "Existing Feature" (added 1 scenario)
|
||||
|
||||
**<capability-2>**:
|
||||
- Created new spec file
|
||||
- Added requirement: "Another Feature"
|
||||
|
||||
Main specs are now updated. The change remains active - archive when implementation is complete.
|
||||
```
|
||||
|
||||
**Guardrails**
|
||||
- Read both delta and main specs before making changes
|
||||
- Preserve existing content not mentioned in delta
|
||||
- If something is unclear, ask for clarification
|
||||
- Show what you're changing as you go
|
||||
- The operation should be idempotent - running twice should give same result
|
||||
@@ -1,7 +1,20 @@
|
||||
[[sources]]
|
||||
id = "main"
|
||||
description = "当前项目库"
|
||||
description = "当前项目库测试环境以及本地环境"
|
||||
dsn = "postgresql://erp_pgsql:erp_2025@cxd.whcxd.cn:16159/junhong_cmp_test?sslmode=disable"
|
||||
lazy = true
|
||||
|
||||
|
||||
[[sources]]
|
||||
id = "pro_main"
|
||||
description = "当前项目库正式环境"
|
||||
dsn = "postgresql://junhong_cmp:CtCom1zBzPQbpVNf3rCNxH@127.0.0.1:5432/junhong_cmp_prod?sslmode=disable"
|
||||
ssh_host = "116.162.101.91"
|
||||
ssh_port = 22
|
||||
ssh_user = "root"
|
||||
ssh_key = "~/.ssh/id_ed25519"
|
||||
lazy = true
|
||||
|
||||
|
||||
[[sources]]
|
||||
id = "legacy_mysql"
|
||||
@@ -22,8 +35,18 @@ source = "main"
|
||||
[[tools]]
|
||||
name = "execute_sql"
|
||||
source = "main"
|
||||
readonly = true # Only allow SELECT, SHOW, DESCRIBE, EXPLAIN
|
||||
max_rows = 1000 # Limit query results
|
||||
readonly = true # Only allow SELECT, SHOW, DESCRIBE, EXPLAIN
|
||||
max_rows = 1000 # Limit query results
|
||||
|
||||
[[tools]]
|
||||
name = "search_objects"
|
||||
source = "pro_main"
|
||||
|
||||
[[tools]]
|
||||
name = "execute_sql"
|
||||
source = "pro_main"
|
||||
readonly = true
|
||||
max_rows = 1000
|
||||
|
||||
[[tools]]
|
||||
name = "search_objects"
|
||||
@@ -32,5 +55,5 @@ source = "legacy_mysql"
|
||||
[[tools]]
|
||||
name = "execute_sql"
|
||||
source = "legacy_mysql"
|
||||
readonly = true # Only allow SELECT, SHOW, DESCRIBE, EXPLAIN
|
||||
max_rows = 5000 # Limit query results for migration exploration
|
||||
readonly = true # Only allow SELECT, SHOW, DESCRIBE, EXPLAIN
|
||||
max_rows = 5000 # Limit query results for migration exploration
|
||||
|
||||
4
.env
4
.env
@@ -5,10 +5,6 @@ DB_USER=erp_pgsql
|
||||
DB_PASSWORD=erp_2025
|
||||
DB_NAME=junhong_cmp_test
|
||||
DB_SSLMODE=disable
|
||||
GOOGLE_GEMINI_BASE_URL="http://45.155.220.179:8317" # 根据实际填写你服务器的ip地址或者域名
|
||||
GEMINI_API_KEY="sk-VoNbvr6aGpjvZX64rvhrwowrZrCgtGuX9oxykIy8F1DBg"
|
||||
GOOGLE_GENAI_USE_GCA="true"
|
||||
GEMINI_MODEL="gemini-3-pro-preview" # 如果你有gemini3权限可以填: gemini-3-pro-preview
|
||||
|
||||
# 七月迭代:Worker、企微 Adapter 与旧审批入口切换
|
||||
JUNHONG_WORKER_ROLE=all
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user