Documentation Authoring Workflow
SkillDocs & knowledgeAuthor or update technical documentation in the repo's `docs/` directory. Docs are the living "as built" reference for how Rock currently works, organized by Rock domain (`docs/core/`, `docs/lava/`, `docs/group/`, etc.). Audience is core developers and quasi-technical community members. Use when the user says "write docs for X", "document this feature", "update the docs", "how is X documented", "audit the docs", or describes a feature/subsystem they want a written reference for. Also use when the spec skill hands off after completing a spec, or when the commit skill detects that changed files are referenced in some doc's frontmatter. Do NOT use for: writing end-user tutorials or how-to guides (different audience), generating API reference (separate tool), writing release notes, drafting RFCs or proposals (those are specs).
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Documentation Authoring Workflow skill
What this skill tells your AI
The instructions your AI receives, as published by sparkdevnetwork/rock in .claude/skills/docs/SKILL.md and read by ahel’s review.
You are creating or maintaining technical documentation in the repo's docs/ directory.
Argument: $ARGUMENTS
Audience and Scope
- Audience: core Rock developers, plugin authors, and quasi-technical community members. Assume the reader can read C# and SQL but does not yet know this corner of Rock.
- Voice: terse, opinionated, code-anchored. Prefer concrete file paths and method names over abstract description. Match the tone of the existing
.claude/rules/*.mdand skill files. - Time horizon: "as built" — the current truth about how Rock works today. NOT a changelog and NOT a museum of past decisions.
Specs vs Docs
| Spec | Doc | |
|---|---|---|
| Tense | "as designed at a point in time" | "as built today" |
| Mutability | Immutable after completion | Continuously updated |
| Audience | Reviewers of a proposed change | Engineers needing to understand current behavior |
| Lives in | specs/ then specs/completed/{domain}/ | docs/{domain}/ |
| Linked from | Other specs (rare) | Source code, other docs, sibling specs |
If the request is really for a spec (capturing a decision before code lands), redirect to the spec skill. If it is really for end-user help, an API reference, or release notes, redirect appropriately.
Decide the Mode
Read $ARGUMENTS and the conversation, then pick one of three modes:
- Create / update mode (default) — write a new doc or update an existing one. Steps 1 through 7 apply.
- Audit mode — given an existing doc path, verify its claims against current source code and report drift. Triggered by phrases like "audit the docs for X", "check if the docs are still accurate", "verify these docs against the code". Skip to Audit Mode at the bottom of this file.
- Update-from-spec mode — invoked by the
specskill in completion mode. The caller passes a completed-spec path and one or more candidate doc paths. Skip to Update-From-Spec Mode at the bottom of this file.
Step 1 — Determine Topic, Domain, and Existing Coverage
1.1 Determine the topic
Read the conversation, recent diffs (if available), the branch name, and $ARGUMENTS to identify what is being documented. Do not invent topics. If the topic is genuinely unclear, ask one question and stop.
1.2 Pick the domain folder
The canonical list of domains, the path-to-domain mapping, and the translation between release-note casing and folder names live in .claude/rules/rock-domains.md. Use that file as the single source of truth. Do not duplicate or guess.
docs/ uses the lowercase-kebab folder form (same convention as specs/completed/). The H1 of docs/{folder}/README.md uses the release-note form for human readability.
If the topic is genuinely cross-cutting and no single domain fits, use other. Do not invent new domain folders.
1.3 Search for existing coverage (mandatory)
Before writing anything new, search docs/ for existing files that already cover this topic. Use both:
Globfor filename matches indocs/{domain}/and across alldocs/.Grepfor keyword matches inside doc bodies (the topic may live under a different filename than expected).
Possible outcomes:
| Result | Action |
|---|---|
| One file already covers it well | Update mode. Use that file. |
| One file partially covers it | Update mode. Extend that file. |
| Multiple overlapping files | Stop. Surface the overlap to the user before proceeding; offer to consolidate or to scope the new file narrowly. |
| No existing coverage | Create mode. Proceed to Step 2. |
State the search result before writing or modifying any file. The user should know whether you are creating or updating, and why.
1.4 Identify cross-cutting concepts before writing
Some topics include concepts that are not specific to this domain but recur across many areas of Rock. These belong in docs/core/ (or another genuinely cross-cutting folder), not embedded in every domain doc that touches them. Examples:
- Entity reference resolution (
GetQueryableByKey, IdKey vs raw int, the "Disable Predictable Ids" site setting). - PersonAlias semantics in audit columns and custom person FKs.
RockContextlifecycle and lazy loading.- Cache invalidation patterns.
- Lava data object conventions (
LavaDataObject, naming withInfosuffix). - The Obsidian block lifecycle, security pattern, and bag conventions.
If the topic includes a cross-cutting concept:
- Search
docs/core/(and any other cross-cutting folder) for existing coverage. - If a core doc covers it, link to it; do not re-explain it inline. Domain docs link, they do not duplicate.
- If no core doc covers it, ask the user before writing whether to (a) create the core doc first and link to it from the current doc, or (b) scope a brief inline explanation here this once. Default to (a) when the concept will obviously recur in other domain docs.
Heuristic: if you would write substantively the same paragraph in three different domain docs, it belongs in docs/core/ instead. The IdKey vs raw-int discussion is a canonical example: every Obsidian block that resolves an entity reference faces the same question, so it lives once in docs/core/entity-reference-resolution.md and every block doc links to it.
Step 2 — Read the Code (mandatory before any "How it works" content)
LLMs cheerfully invent plausible-sounding documentation that is wrong. Do not infer mechanics from the conversation. Before writing or updating any "How It Works", "Data Model", "Extension Points", or "Limitations" section, read the actual source files.
Minimum source reading:
- The primary entity / service / block class being documented.
- Any service class methods called out as part of the topic.
- The
usinggraph one level out (the immediate dependencies).
Pull file:line references into the doc as you write. Every non-trivial mechanical claim in the doc should be traceable to a specific path in the codebase.
If the source has been refactored since the topic was last documented (e.g. the conversation references method names that no longer exist), say so and ask before proceeding.
Sources for the "why"
Code answers "what." Engineering notes, commit messages, and specs answer "why." For any non-trivial behavior the doc describes, find the why and lead with it. The "Why It Exists" and "Key Architectural Decisions" sections of the doc live or die on this step.
Three primary sources, in order of usefulness:
-
Engineering notes in the source files. Rock convention is multi-line comments with the developer's name or initials (often a
Reason:one-liner). Pattern from CLAUDE.md:/* 3/5/26 - JE <Why this code exists or why this change was made.> Reason: <One-line summary for scanning.> */These are the highest-signal source. Grep
related_filesfor blocks like this and quote or paraphrase the reason directly into the doc. The original phrasing is often better than anything you could rewrite. -
Recent commit messages on
related_files. Rungit log --format="%h %ai %s%n%n%b" -- {related_files}and read the bodies, not just the subjects. Release-note commits (subjects starting with+ () are written for an external audience and frequently include the why. -
Linked specs in
related_specs. Specs inspecs/completed/capture the design intent and the alternatives that were rejected. The Motivation and Considered but Rejected sections of a spec map directly to the corresponding sections of the doc.
If you cannot find a why for a specific design choice, that is a finding worth surfacing to the user. Do not invent one. A doc that says "the design rationale is unclear, see {file:line}; consider asking {original author}" is more useful than a doc that confidently fabricates.
Step 3 — Filename and Path
docs/{domain}/{topic-kebab}.md
- Domain folder: lowercase-kebab as defined above.
- File name: lowercase, hyphens for spaces. Strip leading articles. No timestamp prefix (unlike specs).
- Names should be noun-phrases scoped to a feature, not verbs and not entire subsystems.
Examples:
| Good | Bad |
|---|---|
docs/group/group-requirements.md | docs/group/groups.md (too broad) |
docs/lava/entity-commands.md | docs/lava/how-to-use-lava.md (verb, end-user voice) |
docs/core/cache-invalidation.md | docs/core/cache.md (likely too broad) |
docs/finance/refund-flow-nmi.md | docs/finance/finance.md (entire domain) |
If a file would exceed roughly 500 lines, split it. One file per coherent concept, not one file per domain.
Step 4 — Write or Update the YAML Frontmatter
Every doc begins with this block:
---
title: Group Requirements
last_updated: 2026-04-28
related_specs:
- specs/completed/group/240118-group-requirement-caching.md
- specs/completed/group/250903-group-requirement-override.md
related_files:
- Rock/Model/Group/GroupRequirement.cs
- Rock/Model/Group/GroupRequirementService.cs
- Rock.Blocks/Group/GroupRequirementsList.cs
---
title— the human-readable title. Mirrors the H1.last_updated— ISO-8601 date of this edit. Bump on every meaningful change. Get withdate +%Y-%m-%d.related_specs— list of completed-spec paths relative to the repo root. Active specs (still inspecs/root) MUST NOT appear here. Used by the auto-rendered Related Specs section, which computes the relative-to-doc form for the markdown link at render time. Omit the key if there are none.related_files— list of source-code paths relative to the repo root that this doc materially depends on. Used by the commit/audit hooks to detect when source has changed and the doc may need refresh, and as the scope for thegit logquery that sources Recent Impactful Changes. Omit the key if there are none.
Both lists store repo-root-relative paths in YAML so they are stable when docs move and so downstream tools (git log, Glob, audit-mode lookups) can consume them without translation.
Step 5 — Write the Body
The structure below is deliberately top-loaded with the why and the conceptual content, and back-loads the dense reference material. A developer who reads only the top half should come away knowing why this exists, how to think about it, and the practical things that bite people. The technical reference at the bottom is for the developer actively writing code in the area.
The audience is core developers and quasi-technical community members. The voice is "what a senior engineer would tell a colleague who just walked into this corner of the codebase." Why first, concept second, gotchas third, code archaeology last.
Sections are recommended but optional. Include only the sections that apply. If a topic needs a section that is not on this list, add it.
# {Title}
## Overview
{2-4 sentences. The 30-second answer to "what is this and why does it matter." A reader scanning the directory should know whether the rest of the doc is relevant after reading this paragraph alone.}
## Why It Exists
{2-4 sentences. The problem this system solves and the cost of not having it. Source the answer from engineering notes in the related source files, recent commit messages on those files, and the Motivation section of any linked specs (see Step 2's "Sources for the why"). Lead with the why; do not bury it under mechanics. Skip this section only if the why is genuinely obvious from the Overview, but err on the side of including it.}
## Mental Model
{How to *think about* this thing. The conceptual shape. Key distinctions, analogies, the "right frame" for reasoning about it. NO file paths in this section. NO line numbers. NO column lists. This is where a developer who has never touched this corner gets oriented in plain language. Two or three short paragraphs is usually enough.}
## What You Need to Know
{The practical rules of the road. The non-obvious behaviors that bite first-time visitors. The conventions a developer absolutely must internalize before they start changing code in this area. Use bold leads on each item or short subheadings when you have several distinct points. Each item should be the kind of thing you would warn a colleague about over coffee. Limit file:line references to the minimum needed to make the warning concrete; the dense references go in Technical Reference at the bottom.}
## Common Scenarios
{Optional. Pattern recipes for common tasks. "How do I add a member to a synced group", "How do I change a requirement type without re-evaluating", "How do I record attendance for a cancelled occurrence". Skip this section entirely if the topic does not have recurring patterns worth showing.}
## Key Architectural Decisions
{Why it is designed this way. Each decision: a short heading and the *why*. Source the why from engineering notes and commit messages whenever possible; quote them when the original phrasing is good. This section helps a developer decide whether to work with the grain of the design or push back on it.}
## Considered but Rejected
{Optional. Alternatives that have been evaluated and dismissed. For each:
- A short heading (the alternative).
- One-line description.
- Why it was rejected.
- A date in (YYYY-MM-DD) form **only if you can verify it from a spec, commit, or comment in source.** Omit the date otherwise; never approximate.
This section preempts re-litigation. Include whenever there were real alternatives, even briefly considered.}
After the template above, the doc continues with the Technical Reference section and the auto-rendered Recent Impactful Changes section. These are described below as prose rather than as nested template blocks, because both have authoring rules that are easier to read outside a {...} placeholder.
Technical Reference
Everything dense, code-anchored, and reference-shaped goes here. A developer actively changing code in this area lives in this section; a developer just trying to understand the system can stop reading at the previous section.
Use subheadings to organize. Suggested subheadings (include only those that apply):
## Technical Reference
### Data Model
{Entities, key columns, FKs and cascade behavior, indices. The dense table. File:line citations.}
### Save Hook Behavior
{Pre-save and post-save logic per state (Added, Modified, Deleted). Validation. Cache invalidation. Workflow triggers.}
### Service / API Surface
{Non-trivial public methods on the service classes. What they do, when to use them.}
### Caching
{How caching applies to this subsystem.}
### Affected Blocks and UI Surfaces
{Block paths (C# and Obsidian). Where this subsystem is consumed in the UI.}
### Extension Points
{Workflow triggers, Lava filters, plugin hooks, configuration attributes.}
### File Index
{A quick path index for the most-relevant files. Useful when a reader knows the topic but cannot remember where the code lives.}
Add or drop subheadings as the topic warrants. A doc on a pure data concept may not have "Save Hook Behavior". A doc on a UI surface may not have "Data Model". That is fine.
Recent Impactful Changes
## Recent Impactful Changes
At most 5 bullets, newest first.
Mandatory: source every entry from git log. Never write a date you have not seen in git output. If you cannot verify it, drop the entry. Do not paraphrase the date, do not write fuzzy values like "2024-late" or "early 2025", do not back-infer from issue numbers in commit subjects.
Recommended command (scopes to related_files from this doc's frontmatter):
git log -n 30 --format="%h|%ai|%s" --since="18 months ago" -- {related_files}
From that output, prefer release-note commits (subject begins with + (). Each bullet:
- **YYYY-MM-DD** ([commit `{short-hash}`](https://github.com/SparkDevNetwork/Rock/commit/{short-hash})). One-line summary, paraphrased from the commit subject. Append `(Fixes #NNNN)` only if the commit subject contains it.
If git log returns fewer than 5 release-note commits in the window, write fewer bullets. Three accurate entries beat five plausible ones. If there are zero, omit the section entirely.
The split between "What You Need to Know" and "Technical Reference"
The most common authoring mistake is to dump every gotcha into a giant "Limitations" section at the bottom. Resist this. A gotcha that every developer working in the area must internalize belongs in What You Need to Know at the top, in plain language. A gotcha that only matters when you are actively touching a specific service method belongs in Technical Reference next to that method.
If you cannot decide where something belongs, put it in What You Need to Know. The cost of telling a developer something they already knew is small; the cost of letting them miss something that bites them is large.
Step 6 — Render the Related Specs Section
If related_specs in the YAML frontmatter is non-empty, render this section at the very bottom of the doc, after every other section:
## Related Specs
- [Group requirement caching](../../specs/completed/group/240118-group-requirement-caching.md) — 2024-01-18 (Daniel Hazelbaker)
- [Group requirement override](../../specs/completed/group/250903-group-requirement-override.md) — 2025-09-03 (Jon Edmiston)
Rules:
- Source the list from
related_specsin the YAML. Do not duplicate the data anywhere else. - Paths in YAML are repo-root-relative; compute the relative-to-doc form at render time for the markdown link so the link is clickable from the doc's location. Do not store the relative form in YAML.
- Only completed specs. Filter to paths under
specs/completed/. If a path inrelated_specsis not undercompleted/, drop it from the rendered list AND warn the user (it is probably a stale or premature link). - Link text is the spec's H1 title (or YAML
titleif there is no H1). Do not use the filename. - Each line: linked title, separator
—(the one sanctioned em-dash use, because the renderer needs a visible separator that is not a hyphen), date from spec's YAMLdate_created, primary author from spec's YAMLauthorin parens. - Sort by date descending (newest first).
- If
related_specsis empty or absent, omit this section entirely. No empty placeholder. - The skill regenerates this section from frontmatter on every doc save. Manual edits inside this section will be overwritten.
Step 7 — Update or Create the Domain README.md
Each docs/{domain}/ folder MUST contain a README.md that indexes every doc file in that folder. The README is fully owned by this skill — regenerate freely on every doc add/rename/remove.
Structure:
# {Domain Title} Documentation
{1 to 2 paragraphs: what this domain covers in Rock, who tends to work in it, where to look in code for the entry points. Plain prose, not a list.}
## Files in this directory
| Doc | Summary |
|---|---|
| [Group Requirements](group-requirements.md) | How group requirements are evaluated, cached, and overridden. |
| [Group Member Workflows](group-member-workflows.md) | Workflows that fire on group member add/remove/role-change. |
- Domain Title in the H1 uses release-note casing (
Group,Lava,CRM,Check-in). The folder name is lowercase; the heading is human-friendly. - The intro prose is regenerated by the skill but should preserve its meaning across regenerations. If you have new prose to add, fold it in rather than replacing wholesale.
- The table lists every
*.mdfile in the folder exceptREADME.mditself, alphabetical by file name. Summary comes from the doc's YAMLsummaryif present, otherwise from the doc's first non-heading paragraph after the H1.
If a docs/README.md exists at the repo-docs root, the skill is also responsible for keeping that one current with the list of domain folders. Same auto-regenerated rules apply.
Step 8 — Output
After writing or updating files:
- State the path of every file written or modified (the doc, its domain README, possibly the root
docs/README.md). - State whether this was a create or update.
- Stop. Do not paste the doc content back into chat.
Style Rules
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 686
- Forks
- 416
- Last commit
- Sep 2026
- Hacker News mentions
- 20
Advanced
- Catalog kind
- skill
- Gateway key
docs-sparkdevnetwork- Source
- github.com/sparkdevnetwork/rock