mirror of
https://gitverse.ru/kpa39l/spec-driven-infra.git
synced 2026-09-29 09:15:03 +00:00
Initial commit: Hermes skill spec-driven-infra
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user