From 185a7a718cb3d3fb493e23096ebbb39194e0b0f9 Mon Sep 17 00:00:00 2001 From: Jan Cibulka Date: Wed, 10 Jun 2026 11:33:36 +0300 Subject: [PATCH] feat(llm): Add `align` skills --- .agents/skills/align/ADR-FORMAT.md | 35 ++++++++++ .agents/skills/align/CONTEXT-FORMAT.md | 36 +++++++++++ .agents/skills/align/SKILL.md | 90 ++++++++++++++++++++++++++ .config/filegen-manifest.json | 33 ++++++++++ llm/skills/align/ADR-FORMAT.md | 35 ++++++++++ llm/skills/align/CONTEXT-FORMAT.md | 36 +++++++++++ llm/skills/align/SKILL.md | 90 ++++++++++++++++++++++++++ nix/general/default.nix | 5 +- nix/general/skills.nix | 20 ++++++ 9 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 .agents/skills/align/ADR-FORMAT.md create mode 100644 .agents/skills/align/CONTEXT-FORMAT.md create mode 100644 .agents/skills/align/SKILL.md create mode 100644 .config/filegen-manifest.json create mode 100644 llm/skills/align/ADR-FORMAT.md create mode 100644 llm/skills/align/CONTEXT-FORMAT.md create mode 100644 llm/skills/align/SKILL.md create mode 100644 nix/general/skills.nix diff --git a/.agents/skills/align/ADR-FORMAT.md b/.agents/skills/align/ADR-FORMAT.md new file mode 100644 index 00000000..d973194c --- /dev/null +++ b/.agents/skills/align/ADR-FORMAT.md @@ -0,0 +1,35 @@ +# ADR format + +ADRs record _that_ a decision was made and _why_. They live in `docs/adr/`, numbered `0001-slug.md`, `0002-slug.md`, … Create the directory only when the first ADR is due. + +## Template + +```md +# {Decision in a short title} + +{1–3 sentences: the situation, what we chose, and why.} +``` + +A single paragraph is enough. The worth is in the record, not in filling sections. + +## Optional sections + +Add only when they earn their place — most ADRs need none: + +- **Status** (`proposed | accepted | deprecated | superseded by ADR-NNNN`) — when decisions get revisited. +- **Options considered** — when the rejected paths are worth remembering. +- **Consequences** — when downstream effects aren't obvious. + +## Numbering + +Take the highest number in `docs/adr/` and add one. + +## When to write one + +All three must hold: + +1. **Costly to undo** — reversing later hurts. +2. **Non-obvious** — a future reader will ask "why this way?" +3. **A real trade-off** — there were live alternatives and you chose one deliberately. + +Easy to reverse? Skip it. Obvious? Nobody will wonder. No alternative? Nothing to record. diff --git a/.agents/skills/align/CONTEXT-FORMAT.md b/.agents/skills/align/CONTEXT-FORMAT.md new file mode 100644 index 00000000..44edf718 --- /dev/null +++ b/.agents/skills/align/CONTEXT-FORMAT.md @@ -0,0 +1,36 @@ +# CONTEXT.md format + +A `CONTEXT.md` is a glossary for one domain — nothing else. It fixes the words the team uses so code, docs, and conversation stay aligned. + +## Shape + +```md +# {Context name} + +{One or two sentences: what this context covers and why it exists.} + +## Language + +**Room**: +A Matrix room: a set of members sharing one event timeline. +_Avoid_: channel, chat, group + +**Practitioner**: +A healthcare professional who acts in the system on behalf of an organization. +_Avoid_: doctor, user, staff + +**Patient**: +The natural person a health record belongs to, distinct from the user operating the system. +_Avoid_: user, account, client +``` + +## Rules + +- **Pick one word per concept.** List the rejected synonyms under `_Avoid_`. +- **One or two sentences.** Say what the term _is_, not what it does. +- **Context-specific only.** Skip general engineering terms (timeouts, error enums, `famedly-rust-utils` helpers). Ask: is this concept unique to this domain? If not, leave it out. +- **Cluster under subheadings** when groups emerge; a flat list is fine for one tight area. + +## Where it lives + +One `CONTEXT.md` at the repo root, created the first time a term is pinned down. If a repo ever grows several distinct domains, drop a `CONTEXT.md` into each subtree and use the one nearest the code in play — no central index needed for now. diff --git a/.agents/skills/align/SKILL.md b/.agents/skills/align/SKILL.md new file mode 100644 index 00000000..cd6b5047 --- /dev/null +++ b/.agents/skills/align/SKILL.md @@ -0,0 +1,90 @@ +--- +name: align +description: Pressure-test a plan or design with the engineer until it is fully aligned, walking every branch of the decision tree and checking it against Famedly's documented language (CONTEXT.md) and decisions (ADRs), updating those docs as agreement lands. Decisions are raised as multiple-choice prompts through the agent's question tool; when aligned, optionally captures the result as a PRD (Notion, Jira, or repo). Use when the user wants to create a plan, yet also invoke this skill proactively before starting any non-trivial change: one carrying genuine design choices or ambiguity, or spanning multiple files, unfamiliar code, or real research. Skip trivial, well-specified edits (a rename, a one-liner, a typo). +--- + + + +Drive the plan to alignment. Take the decision tree branch by branch, settle dependencies before the choices that rest on them, and put a recommended answer on every decision. + +Answer your own questions from the code first — search the repo, read the relevant crate/module — and only bring me what truly needs my intent or domain knowledge. + + + + + +Engage proactively, before starting the work — not only on an explicit "align" invocation. A change is worth aligning on when it carries genuine design choices or ambiguity, or when it spans multiple files, unfamiliar code, or real research. A trivial, well-specified edit — a rename, a one-liner, a typo fix — needs none of this; just do it. + +Right-size the effort to the task. Scale the number of rounds and the depth of grilling to the ambiguity and blast radius: a medium, mostly-clear change may need a single round on the one real decision, while a large or open-ended one earns the full tree. If exploring the code shows the task is actually trivial or already unambiguous, say so and drop straight into the work rather than manufacturing questions. + + + + + +Raise decisions through whatever structured question tool the agent runtime exposes: + +- **Cursor** → `AskQuestion` or similar, +- **Claude Code / Claude CLI** → `AskUserQuestion` or similar, +- **none tool available** → ask questions in chat one by one. + +For each question: + +1. Group related but independent decisions together (1–5 per round). Resolve upstream choices first; only ask dependent follow-ups once their parent is settled. +2. Lead with your recommended option, tag it `Recommended:`, and justify it in one line. +3. Give at least two real options plus a way for user to specify their own answer. Allow multi-select when several answers can hold at once. +4. Keep rounds going until the tree is exhausted, then restate the settled decisions back to me. + + + + + +Once the tree is settled, summarize the agreed decisions. Then ask me — via the same question tool — how to capture them: + +- **Keep in chat only** — the alignment lives in this conversation; write nothing. +- **Write a PRD** — produce the structured doc below. Then ask _where_: a Notion page, a Jira ticket, or a markdown file in the repo. Default to whatever I've used for this work already. + +Synthesize the PRD from what we settled — do not re-interview me. Use `CONTEXT.md` vocabulary throughout; keep file paths and code snippets out (they rot), except a short snippet that pins a precise decision (a type, schema, or state machine). + +```md +# {Title} + +## Problem + +{The problem, from the user's view.} + +## Solution + +{The chosen approach, from the user's view.} + +## User stories + +1. As a {actor}, I want {capability}, so that {benefit}. + {Cover every facet we aligned on.} + +## Decisions + +{Modules touched, interfaces, schema/API contracts, trade-offs settled, the test seams (prefer existing, highest possible).} + +## Out of scope + +{What we deliberately excluded.} +``` + + + + + +While exploring, pull in the project's domain docs and verify the plan against them. + +**Locate them:** expect a `CONTEXT.md` plus `docs/adr/` at the repo root (or the nearest one to the code in play). Write these files only once you have something concrete to record and I've confirmed docs should be created. + +During the alignment: + +- **Term clashes** — when I use a word against its `CONTEXT.md` meaning, stop me: "Glossary says 'room' is X; you mean Y — which holds?" +- **Vague words** — offer a precise canonical term ("'user' — the account, the Matrix user, or the patient?"). +- **Edge scenarios** — invent concrete cases (federation, multi-tenant, gematik/ePA flows, GDPR deletion) to force sharp boundaries. +- **Code reality** — when a claim conflicts with the code, name the contradiction. +- **Record terms live** in `CONTEXT.md` as they settle — never batch. It stays a pure glossary: no implementation, specs, or notes. See [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md). +- **Suggest an ADR only** when the call is costly to undo, non-obvious to a later reader, and a genuine trade-off. Otherwise skip. See [ADR-FORMAT.md](./ADR-FORMAT.md). + + diff --git a/.config/filegen-manifest.json b/.config/filegen-manifest.json new file mode 100644 index 00000000..30446d3f --- /dev/null +++ b/.config/filegen-manifest.json @@ -0,0 +1,33 @@ +{ + "clobber-by-default": null, + "files": [ + { + "clobber": true, + "deactivate": null, + "ignore-modification": null, + "permissions": "600", + "source": "/nix/store/is33zqwvd62zv52jjjk2xqf34p2n5hpz-ADR-FORMAT.md", + "target": "./.agents/skills/align/ADR-FORMAT.md", + "type": "copy" + }, + { + "clobber": true, + "deactivate": null, + "ignore-modification": null, + "permissions": "600", + "source": "/nix/store/1rd8h8d3nniwnzm8lvq999l439v3vppl-CONTEXT-FORMAT.md", + "target": "./.agents/skills/align/CONTEXT-FORMAT.md", + "type": "copy" + }, + { + "clobber": true, + "deactivate": null, + "ignore-modification": null, + "permissions": "600", + "source": "/nix/store/2wq7jci81iq1976yaghqais7cbd38j4p-SKILL.md", + "target": "./.agents/skills/align/SKILL.md", + "type": "copy" + } + ], + "version": 3 +} diff --git a/llm/skills/align/ADR-FORMAT.md b/llm/skills/align/ADR-FORMAT.md new file mode 100644 index 00000000..d973194c --- /dev/null +++ b/llm/skills/align/ADR-FORMAT.md @@ -0,0 +1,35 @@ +# ADR format + +ADRs record _that_ a decision was made and _why_. They live in `docs/adr/`, numbered `0001-slug.md`, `0002-slug.md`, … Create the directory only when the first ADR is due. + +## Template + +```md +# {Decision in a short title} + +{1–3 sentences: the situation, what we chose, and why.} +``` + +A single paragraph is enough. The worth is in the record, not in filling sections. + +## Optional sections + +Add only when they earn their place — most ADRs need none: + +- **Status** (`proposed | accepted | deprecated | superseded by ADR-NNNN`) — when decisions get revisited. +- **Options considered** — when the rejected paths are worth remembering. +- **Consequences** — when downstream effects aren't obvious. + +## Numbering + +Take the highest number in `docs/adr/` and add one. + +## When to write one + +All three must hold: + +1. **Costly to undo** — reversing later hurts. +2. **Non-obvious** — a future reader will ask "why this way?" +3. **A real trade-off** — there were live alternatives and you chose one deliberately. + +Easy to reverse? Skip it. Obvious? Nobody will wonder. No alternative? Nothing to record. diff --git a/llm/skills/align/CONTEXT-FORMAT.md b/llm/skills/align/CONTEXT-FORMAT.md new file mode 100644 index 00000000..44edf718 --- /dev/null +++ b/llm/skills/align/CONTEXT-FORMAT.md @@ -0,0 +1,36 @@ +# CONTEXT.md format + +A `CONTEXT.md` is a glossary for one domain — nothing else. It fixes the words the team uses so code, docs, and conversation stay aligned. + +## Shape + +```md +# {Context name} + +{One or two sentences: what this context covers and why it exists.} + +## Language + +**Room**: +A Matrix room: a set of members sharing one event timeline. +_Avoid_: channel, chat, group + +**Practitioner**: +A healthcare professional who acts in the system on behalf of an organization. +_Avoid_: doctor, user, staff + +**Patient**: +The natural person a health record belongs to, distinct from the user operating the system. +_Avoid_: user, account, client +``` + +## Rules + +- **Pick one word per concept.** List the rejected synonyms under `_Avoid_`. +- **One or two sentences.** Say what the term _is_, not what it does. +- **Context-specific only.** Skip general engineering terms (timeouts, error enums, `famedly-rust-utils` helpers). Ask: is this concept unique to this domain? If not, leave it out. +- **Cluster under subheadings** when groups emerge; a flat list is fine for one tight area. + +## Where it lives + +One `CONTEXT.md` at the repo root, created the first time a term is pinned down. If a repo ever grows several distinct domains, drop a `CONTEXT.md` into each subtree and use the one nearest the code in play — no central index needed for now. diff --git a/llm/skills/align/SKILL.md b/llm/skills/align/SKILL.md new file mode 100644 index 00000000..cd6b5047 --- /dev/null +++ b/llm/skills/align/SKILL.md @@ -0,0 +1,90 @@ +--- +name: align +description: Pressure-test a plan or design with the engineer until it is fully aligned, walking every branch of the decision tree and checking it against Famedly's documented language (CONTEXT.md) and decisions (ADRs), updating those docs as agreement lands. Decisions are raised as multiple-choice prompts through the agent's question tool; when aligned, optionally captures the result as a PRD (Notion, Jira, or repo). Use when the user wants to create a plan, yet also invoke this skill proactively before starting any non-trivial change: one carrying genuine design choices or ambiguity, or spanning multiple files, unfamiliar code, or real research. Skip trivial, well-specified edits (a rename, a one-liner, a typo). +--- + + + +Drive the plan to alignment. Take the decision tree branch by branch, settle dependencies before the choices that rest on them, and put a recommended answer on every decision. + +Answer your own questions from the code first — search the repo, read the relevant crate/module — and only bring me what truly needs my intent or domain knowledge. + + + + + +Engage proactively, before starting the work — not only on an explicit "align" invocation. A change is worth aligning on when it carries genuine design choices or ambiguity, or when it spans multiple files, unfamiliar code, or real research. A trivial, well-specified edit — a rename, a one-liner, a typo fix — needs none of this; just do it. + +Right-size the effort to the task. Scale the number of rounds and the depth of grilling to the ambiguity and blast radius: a medium, mostly-clear change may need a single round on the one real decision, while a large or open-ended one earns the full tree. If exploring the code shows the task is actually trivial or already unambiguous, say so and drop straight into the work rather than manufacturing questions. + + + + + +Raise decisions through whatever structured question tool the agent runtime exposes: + +- **Cursor** → `AskQuestion` or similar, +- **Claude Code / Claude CLI** → `AskUserQuestion` or similar, +- **none tool available** → ask questions in chat one by one. + +For each question: + +1. Group related but independent decisions together (1–5 per round). Resolve upstream choices first; only ask dependent follow-ups once their parent is settled. +2. Lead with your recommended option, tag it `Recommended:`, and justify it in one line. +3. Give at least two real options plus a way for user to specify their own answer. Allow multi-select when several answers can hold at once. +4. Keep rounds going until the tree is exhausted, then restate the settled decisions back to me. + + + + + +Once the tree is settled, summarize the agreed decisions. Then ask me — via the same question tool — how to capture them: + +- **Keep in chat only** — the alignment lives in this conversation; write nothing. +- **Write a PRD** — produce the structured doc below. Then ask _where_: a Notion page, a Jira ticket, or a markdown file in the repo. Default to whatever I've used for this work already. + +Synthesize the PRD from what we settled — do not re-interview me. Use `CONTEXT.md` vocabulary throughout; keep file paths and code snippets out (they rot), except a short snippet that pins a precise decision (a type, schema, or state machine). + +```md +# {Title} + +## Problem + +{The problem, from the user's view.} + +## Solution + +{The chosen approach, from the user's view.} + +## User stories + +1. As a {actor}, I want {capability}, so that {benefit}. + {Cover every facet we aligned on.} + +## Decisions + +{Modules touched, interfaces, schema/API contracts, trade-offs settled, the test seams (prefer existing, highest possible).} + +## Out of scope + +{What we deliberately excluded.} +``` + + + + + +While exploring, pull in the project's domain docs and verify the plan against them. + +**Locate them:** expect a `CONTEXT.md` plus `docs/adr/` at the repo root (or the nearest one to the code in play). Write these files only once you have something concrete to record and I've confirmed docs should be created. + +During the alignment: + +- **Term clashes** — when I use a word against its `CONTEXT.md` meaning, stop me: "Glossary says 'room' is X; you mean Y — which holds?" +- **Vague words** — offer a precise canonical term ("'user' — the account, the Matrix user, or the patient?"). +- **Edge scenarios** — invent concrete cases (federation, multi-tenant, gematik/ePA flows, GDPR deletion) to force sharp boundaries. +- **Code reality** — when a claim conflicts with the code, name the contradiction. +- **Record terms live** in `CONTEXT.md` as they settle — never batch. It stays a pure glossary: no implementation, specs, or notes. See [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md). +- **Suggest an ADR only** when the call is costly to undo, non-obvious to a later reader, and a genuine trade-off. Otherwise skip. See [ADR-FORMAT.md](./ADR-FORMAT.md). + + diff --git a/nix/general/default.nix b/nix/general/default.nix index 88fca352..9523dd5d 100644 --- a/nix/general/default.nix +++ b/nix/general/default.nix @@ -5,7 +5,10 @@ ... }: importingFlake: { - imports = [ ./action-versions.nix ]; + imports = [ + ./action-versions.nix + ./skills.nix + ]; config.perSystem = { config, ... }: diff --git a/nix/general/skills.nix b/nix/general/skills.nix new file mode 100644 index 00000000..191ece57 --- /dev/null +++ b/nix/general/skills.nix @@ -0,0 +1,20 @@ +_: { + perSystem = + { lib, ... }: + let + skillsSrc = ../../llm/skills; + prefix = "${toString skillsSrc}/"; + in + { + # Distribute the vendored agent skills into every environment under the + # cross-client `.agents/skills/` directory. smfh's `copy` only accepts + # file sources, so we emit one entry per file; parent directories are + # created automatically during activation. + filegen.settings.files = map (file: { + type = "copy"; + target = "./.agents/skills/${lib.removePrefix prefix (toString file)}"; + source = file; + clobber = true; + }) (lib.filesystem.listFilesRecursive skillsSrc); + }; +}