Initial commit: Hermes skill spec-driven-infra

This commit is contained in:
estorozhenko
2026-09-06 13:51:26 +00:00
commit ea94ca7ed7
4 changed files with 304 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
---
name: spec-driven-infra
description: >-
OpenSpec spec-driven workflow for infrastructure tasks.
category: software-development
tags:
- openspec
- spec-driven
- infrastructure
- hermata
- planning
- sdlc
---
# Spec-Driven Infrastructure Configuration (OpenSpec for Infra)
## When to Use This Skill
- User wants to build/configure infrastructure (homelab, VPS, systemd units, docker-compose services, /opt/<service> layout) and wants a **spec-first** workflow: decide WHAT before changing anything
- User references OpenSpec (https://openspec.dev, repo Fission-AI/OpenSpec) and wants to apply it to infra tasks rather than code tasks
- Need: proposal → design → specs (delta) → tasks → implement → archive loop, with the source of truth being main specs
- Hermes is the AI assistant driving the workflow (OpenSpec natively supports Hermes)
## Core Concepts
OpenSpec = CLI (terminal) + AI assistant skills (chat). The AI does the plan/design/implementation; the CLI validates, lists, archives, and merges delta specs.
- `openspec init --tools hermes` → writes 6 skills to `.hermes/skills/openspec-*/SKILL.md` (propose, explore, apply, update, sync, archive). No command adapter for Hermes — invoke by skill name (`/openspec-propose` etc.)
- Hermes loads skills from `~/.hermes/skills/` by default. For project-local OpenSpec skills, add the project's `.hermes/skills/` to `skills.external_dirs` in `~/.hermes/config.yaml`:
```bash
hermes config set skills.external_dirs '["/opt/hermes/openspec-lab/.hermes/skills"]'
```
- Structure:
```
openspec/
specs/<capability>/spec.md # source of truth (current behavior)
changes/<change>/ # one folder per proposed change
proposal.md # why & what
specs/<capability>/spec.md # DELTA: ADDED / MODIFIED / REMOVED requirements
design.md # how (files, commands, rollback)
tasks.md # implementation checklist (checkboxes)
config.yaml # context + rules injected into AI prompts
```
## The Loop (proven on infra)
```
/openspec-propose "your infra idea" → creates change + 4 artifacts
/openspec-apply-change <name> → implements tasks (checkboxes)
/openspec-archive-change <name> → merges delta into main specs, moves to changes/archive/<date>-<name>/
```
Terminal commands:
```bash
openspec init --tools hermes --force --no-animation # or: openspec init --tools hermes
openspec new change "<kebab-case-name>" # scaffold a change
openspec status --change "<name>" --json # artifact graph, applyRequires, actionContext
openspec instructions <artifact-id> --change "<name>" --json # per-artifact template + context + rules
openspec validate <name> # must pass before archiving
openspec archive <name> --yes # merge + move to archive
openspec list # active changes
openspec schemas --json # available workflow schemas
```
## Making It Infra-Specific (config.yaml)
The default `spec-driven` schema works for infra, but tune `openspec/config.yaml` so the AI produces infra-flavored artifacts:
```yaml
schema: spec-driven
context: |
Домен: self-hosted инфраструктура (homelab / VPS), Linux (Ubuntu/Debian), Docker (docker compose), systemd.
Управление: systemd-юниты, docker-compose.yml, конфиги в /opt/<service>/; рестарт сервисов — только извне (SSH sudo systemctl restart).
Соглашения: отдельный каталог /opt/<service>/ на сервис; секреты в .env; порты фиксируются в README.md; апдейты через git.
rules:
proposal:
- Указывать затронутые сервисы и порты
- Включать план отката (rollback)
specs:
- Требования в формате MUST/SHOULD/MAY
- Сценарии проверки: GIVEN/WHEN/THEN с конкретными командами верификации (systemctl status, curl, docker ps)
design:
- Указывать конкретные файлы конфигов и юнитов
- Включать команды применения и проверки
tasks:
- Каждая задача — проверяемый шаг с командой верификации
```
- `context` is injected into every artifact instruction (`<context>...</context>`), `rules` per-artifact
- The GIVEN/WHEN/THEN + verification-command pattern maps perfectly onto infra: e.g. "WHEN checking connectivity through 127.0.0.1:1080 → THEN curl returns HTTP 200"
## Pitfalls
- **Artifact `specs` rules warning:** OpenSpec CLI 1.12.0 prints `Rules for 'specs' must be an array of strings, ignoring this artifact's rules` even when rules are a list of strings. The `specs` artifact is special (multi-file dir); the warning is harmless and context/rules for proposal/design/tasks still work. Don't chase it.
- **`archive` requires completed tasks:** `openspec archive <name>` refuses with incomplete tasks unless `--yes` (and even then warns). Finish/check tasks first: `sed -i 's/^- \[ \]/- [x]/' tasks.md`.
- **`--no-init-git` is not a CLI flag** in `openspec init` (v1.12.0). Use `openspec init --tools hermes --force --no-animation` (git init happens by default; acceptable in a lab).
- **JSON pipes:** `openspec ... --json` may print warnings to stderr; redirect separately (`> out.json 2> err.txt`). Don't pipe straight into python without checking stderr.
- **Skill autocomplete:** skills-only tools (Hermes) never show `/opsx` autocomplete; invoke by full skill name.
- **Existing changes:** `openspec new change` with an existing name → ask user whether to continue or create new.
- **`skip_specs: true`** in `.openspec.yaml` for pure refactor/docs changes with no behavior delta — `openspec validate` rejects zero-delta changes otherwise.
## Verification Checklist (after a change)
```bash
openspec status --change "<name>" # all 4 artifacts done
openspec validate "<name>" # valid
openspec list # shows active change with task count
# after archive:
cat openspec/specs/<capability>/spec.md # delta merged into main spec
ls openspec/changes/archive/ # dated archive folder
```
## Hermes Integration Notes
- Install CLI: `npm install -g @fission-ai/openspec@latest` (node 22 present); binary lands at `/opt/hermes/.hermes/node/bin/openspec`
- Generated skills have `allowed-tools: Bash(openspec:*)` — the agent runs `openspec ...` in terminal; it does NOT call the CLI as a library
- The AI reads per-artifact instructions via `openspec instructions <id> --change <name> --json` and writes files to `resolvedOutputPath`
- Test full cycle in a lab dir first (`/opt/hermes/openspec-lab`) before production
- Hermes supported Tools table (docs/supported-tools.md) lists Hermes under "skills only" delivery: `.hermes/skills/openspec-*/SKILL.md`
## References & Templates
- See `references/openspec-infra-test-2026-09.md` for the full hands-on validation transcript (add-vpn-tunnel-proxy example, config.yaml, commands, outputs)
- See `templates/infra-config.yaml` for a ready-to-copy infraOpenSpec config
+103
View File
@@ -0,0 +1,103 @@
# OpenSpec for Infra — Hands-on Validation (2026-09)
Full end-to-end test on this host. All commands verified.
## Setup
```bash
# node 22 present at ~/.local/bin
npm install -g @fission-ai/openspec@latest # v1.12.0, binary -> /opt/hermes/.hermes/node/bin/openspec
mkdir -p /opt/hermes/openspec-lab && cd /opt/hermes/openspec-lab
openspec init --tools hermes --force --no-animation
# Output: "6 skills in .hermes/", "Commands skipped for: hermes (no adapter)"
# Structure created:
# .hermes/skills/openspec-{propose,explore,apply-change,update-change,sync-specs,archive-change}/SKILL.md
# openspec/config.yaml
# openspec/specs/.gitkeep, openspec/changes/archive/.gitkeep
```
Hermes wiring:
```bash
hermes config set skills.external_dirs '["/opt/hermes/openspec-lab/.hermes/skills"]'
hermes config get skills # -> external_dirs: '["..."]'
```
## Infra config.yaml (working)
```yaml
schema: spec-driven
context: |
Домен: self-hosted инфраструктура (homelab / VPS), Linux (Ubuntu/Debian), Docker (docker compose), systemd.
Окружение: Hermes-агент на хосте, SSH-доступ к VPS.
Ключевые сервисы: prosody (XMPP + Slidge мост), netbox (docker), garage (S3), searxng, mtproto-proxy, nntp, tproxy-web-proxy, telegram-tunnel.
Управление: systemd-юниты, docker-compose.yml, конфиги в /opt/<service>/; рестарт сервисов — только извне (SSH sudo systemctl restart).
Соглашения: отдельный каталог /opt/<service>/ на сервис; секреты в .env; порты фиксируются в README.md; апдейты через git.
rules:
proposal:
- Указывать затронутые сервисы и порты
- Включать план отката (rollback)
specs:
- Требования в формате MUST/SHOULD/MAY
- Сценарии проверки: GIVEN/WHEN/THEN с конкретными командами верификации (systemctl status, curl, docker ps)
design:
- Указывать конкретные файлы конфигов и юнитов
- Включать команды применения и проверки
tasks:
- Каждая задача — проверяемый шаг с командой верификации
```
## Change lifecycle test (add-vpn-tunnel-proxy)
```bash
openspec new change add-vpn-tunnel-proxy
# -> openspec/changes/add-vpn-tunnel-proxy/.openspec.yaml (schema: spec-driven, created: 2026-09-06)
openspec status --change add-vpn-tunnel-proxy --json
# artifacts: proposal, specs (outputPath "specs/**/*.md"), design, tasks
# applyRequires: ["tasks"], planningHome.root, actionContext.allowedEditRoots
openspec instructions proposal --change add-vpn-tunnel-proxy --json
# -> instruction (Why/What Changes/Capabilities/Impact), template, context (injected), rules
# NOTE: stderr warns "Rules for 'specs' must be an array of strings, ignoring this artifact's rules" — harmless
```
Wrote the 4 artifacts by hand (proposal.md, specs/tunnel-proxy/spec.md with ADDED requirements + GIVEN/WHEN/THEN scenarios, design.md with files/commands/rollback, tasks.md with checkboxes).
```bash
openspec validate add-vpn-tunnel-proxy # -> "Change 'add-vpn-tunnel-proxy' is valid"
openspec status --change add-vpn-tunnel-proxy # -> 4/4 artifacts complete
openspec list # -> add-vpn-tunnel-proxy 0/7 tasks
# Without --yes, archive fails (asks y/N, no stdin):
openspec archive add-vpn-tunnel-proxy # -> error: 7 incomplete tasks, no answer from stdin
sed -i 's/^- \[ \]/- [x]/' openspec/changes/add-vpn-tunnel-proxy/tasks.md
openspec archive add-vpn-tunnel-proxy --yes
# -> "Specs updated successfully. Change archived as '2026-09-06-add-vpn-tunnel-proxy'."
# -> openspec/specs/tunnel-proxy/spec.md created (2 requirements merged)
# -> openspec/changes/archive/2026-09-06-add-vpn-tunnel-proxy/ (all 4 artifacts)
```
Merged main spec (auto-generated):
```markdown
# tunnel-proxy Specification
## Purpose
TBD - created by archiving change add-vpn-tunnel-proxy. Update Purpose after archive.
## Requirements
### Requirement: Persistent SOCKS5 Tunnel
The system MUST maintain a persistent SOCKS5 proxy tunnel from the host to VPS01, listening on 127.0.0.1:1080.
#### Scenario: Tunnel starts on boot
- GIVEN the host boots ...
#### Scenario: Tunnel dies ...
```
## Gotchas hit
1. `openspec init --no-init-git` → "unknown option" — that flag does not exist in v1.12.0 (git init runs by default).
2. `openspec instructions ... --json | python3 -m json.tool` → JSON decode error because warnings go to **stdout**, not stderr. Use `> out.json 2> err.txt`.
3. Rules for `specs` artifact always warn (even well-formed) — cosmetic, ignore.
4. Archive needs tasks done → use `--yes` after completing tasks.
5. `hermes config get skills` prints external_dirs as a string — normal.
@@ -0,0 +1,52 @@
# Post-hoc documentation & WIP with OpenSpec (2026-09-06 session)
Two real patterns proven on infra tasks: recording ALREADY-DONE work, and
leaving a change honestly open when verification is blocked.
## Pattern 1: Post-hoc documentation of completed work
OpenSpec normally drives work *before* implementation, but it also records
work that is **already done** (hotfix, config tuned before spec). Flow:
1. `openspec new change "<kebab-case-name>"`
2. Write the 4 artifacts **describing what was done**:
- `proposal.md` — why + what changed (past tense fine)
- `specs/<cap>/spec.md` — delta requirements ADDED, with GIVEN/WHEN/THEN scenarios
- `design.md` — approach, files touched, commands, rollback
- `tasks.md` — every step ticked `[x]` **only if genuinely done & verified**
3. `openspec validate <name>` (must pass)
4. `openspec archive <name> --yes` → delta merges into `openspec/specs/<cap>/spec.md`,
change moves to `openspec/changes/archive/<date>-<name>/`
Result: clean dated record; main specs gain the requirement. Proven with
`tavily-proxy-setup` (Tavily web_extract via local HTTP→SOCKS5 forwarder —
3 requirements merged, archived cleanly).
## Pattern 2: Keep WIP open when verification is blocked
`openspec archive` refuses incomplete tasks. When some tasks can't be
verified (network tests needing user presence, flaky external API, blocked
commands), **do not force-archive with `--yes`** — you'd be recording
unverified work as done.
Instead leave the change in `changes/<name>/`. It is a valid WIP state:
```
openspec status --change <name>
Progress: 4/4 artifacts complete
[x] proposal [x] specs [x] design [x] tasks
# but tasks.md still has un-checked boxes → resume point is obvious
```
Next session: `openspec status --change <name>` shows what's left; finish
tasks, `openspec validate`, `openspec archive`. Proven with `local-extractor`
(2 network tests blocked while user was away → change left open, honest).
## Layout traps
- Archived changes live in `openspec/changes/archive/<date>-<name>/` —
**nested under `changes/`**, not a top-level `openspec/archive/`.
`find openspec -name design.md` to locate them after archiving.
- Editing an archived change afterwards = editing
`openspec/changes/archive/<date>-<name>/design.md` (documentation updates
for accuracy), not the pre-archive path.
+23
View File
@@ -0,0 +1,23 @@
# Copy this to openspec/config.yaml and adjust to your infra.
# Injected into every OpenSpec artifact instruction as <context>…</context>.
schema: spec-driven
context: |
Домен: self-hosted инфраструктура (homelab / VPS), Linux (Ubuntu/Debian), Docker (docker compose), systemd.
Окружение: Hermes-агент на хосте, SSH-доступ к VPS (gitverse.ru как источник истины, gitea на bigbox — зеркало).
Ключевые сервисы: prosody (XMPP + Slidge мост), netbox (docker), garage (S3), searxng, mtproto-proxy, nntp, tproxy-web-proxy, telegram-tunnel.
Управление: systemd-юниты, docker-compose.yml, конфиги в /opt/<service>/; рестарт сервисов — только извне (SSH sudo systemctl restart).
Соглашения: отдельный каталог /opt/<service>/ на сервис; секреты в .env; порты фиксируются в README.md; апдейты через git (источник истины gitverse.ru).
rules:
proposal:
- Указывать затронутые сервисы и порты
- Включать план отката (rollback)
specs:
- Требования в формате MUST/SHOULD/MAY
- Сценарии проверки: GIVEN/WHEN/THEN с конкретными командами верификации (systemctl status, curl, docker ps)
design:
- Указывать конкретные файлы конфигов и юнитов
- Включать команды применения и проверки
tasks:
- Каждая задача — проверяемый шаг с командой верификации