Skip to content

Commit e2602ec

Browse files
committed
doc: enriched published agentic instructions (skills)
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
1 parent da7425e commit e2602ec

6 files changed

Lines changed: 528 additions & 1 deletion

File tree

.claude/.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
plans/
2-
skills/
2+
!skills/
3+
skills/.local-skills/
34
commands/
45
agents/
56
hooks/
7+
settings.local.json

.claude/CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ This is a mono-repo with multiple Go modules tied together by `go.work`.
3636
| `internal/difflib/` | Internalized go-difflib for generating diffs |
3737
| `internal/fdleak/` | File descriptor leak detection |
3838
| `internal/leak/` | Goroutine leak detection |
39+
| `internal/tools` | Internal tools exposed |
3940
| `enable/stubs/` | Public API for enabling optional features (yaml, colors) |
4041

4142
### Code generation architecture
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Adding a New Assertion
2+
3+
Step-by-step workflow for adding a new assertion function to testify.
4+
5+
## Workflow
6+
7+
1. Add function to the appropriate domain file in `internal/assertions/`
8+
2. Add `// Domain: <domain>` as the first line inside the function body
9+
3. Add `// Opposite: <Name>` on the next line if a logical opposite exists
10+
4. Add `Examples:` section to the doc comment
11+
5. Add tests to the corresponding `*_test.go` file
12+
6. Run `go generate ./...` to produce all 8 variants + docs
13+
7. Run `go test work ./...` to verify everything
14+
15+
## Function template
16+
17+
```go
18+
// FuncName asserts that <what it does>.
19+
//
20+
// # Usage
21+
//
22+
// assertions.FuncName(t, arg1, arg2)
23+
//
24+
// # Examples
25+
//
26+
// success: arg1Value, arg2Value
27+
// failure: arg1Value, arg2Value
28+
func FuncName(t T, arg1, arg2 any, msgAndArgs ...any) bool {
29+
// Domain: <domain>
30+
// Opposite: <OppositeName> (omit if none)
31+
if h, ok := t.(H); ok {
32+
h.Helper()
33+
}
34+
35+
// implementation
36+
if !condition {
37+
return Fail(t, "message", msgAndArgs...)
38+
}
39+
40+
return true
41+
}
42+
```
43+
44+
## Doc comment annotations
45+
46+
### Domain tag (in doc comment header)
47+
48+
```go
49+
// domain: equality
50+
```
51+
52+
Assigns the function to a documentation domain. Add domain descriptions in
53+
`internal/assertions/doc.go` if creating a new domain.
54+
55+
### Domain comment (inside function body)
56+
57+
```go
58+
// Domain: equality
59+
```
60+
61+
First line inside the function body. Used by the codegen scanner.
62+
63+
### Opposite annotation
64+
65+
```go
66+
// Opposite: NotEqual
67+
```
68+
69+
Second line inside the body (after Domain). Only on the affirmative form
70+
(e.g., on `Equal`, not on `NotEqual`).
71+
72+
### Examples section
73+
74+
```go
75+
// # Examples
76+
//
77+
// success: 123, 123
78+
// failure: 123, 456
79+
```
80+
81+
Drives generated smoke tests for all 8 variants. Three case types:
82+
- `success: <args>` -- test should pass
83+
- `failure: <args>` -- test should fail
84+
- `panic: <args>` followed by `<expected panic message>` on next line
85+
86+
For complex values that can't be represented inline, use `// NOT IMPLEMENTED`:
87+
```go
88+
// success: &customStruct{Field: "value"}, // NOT IMPLEMENTED
89+
```
90+
91+
**Never use `// TODO`** -- it triggers false positives in code quality tools.
92+
93+
## What gets generated
94+
95+
From a single function, `go generate` produces:
96+
97+
| Variant | Package | Example |
98+
|---------|---------|---------|
99+
| Package-level | `assert` | `assert.FuncName(t, ...)` |
100+
| Formatted | `assert` | `assert.FuncNamef(t, ..., "msg")` |
101+
| Forward method | `assert` | `a.FuncName(...)` |
102+
| Forward formatted | `assert` | `a.FuncNamef(..., "msg")` |
103+
| Package-level | `require` | `require.FuncName(t, ...)` |
104+
| Formatted | `require` | `require.FuncNamef(t, ..., "msg")` |
105+
| Forward method | `require` | `r.FuncName(...)` |
106+
| Forward formatted | `require` | `r.FuncNamef(..., "msg")` |
107+
108+
Plus: tests for all variants, documentation entry in `docs/doc-site/api/<domain>.md`.
109+
110+
Generic assertions (with type params) produce 4 variants (no forward methods --
111+
Go limitation).
112+
113+
## Checklist
114+
115+
- [ ] Function in `internal/assertions/<domain>.go`
116+
- [ ] `// Domain:` and optionally `// Opposite:` inside function body
117+
- [ ] Doc comment with `# Usage`, `# Examples` sections
118+
- [ ] Tests in `internal/assertions/<domain>_test.go`
119+
- [ ] `go generate ./...` succeeds
120+
- [ ] `go test work ./...` passes
121+
- [ ] `golangci-lint run --new-from-rev master` clean

.claude/skills/codegen/SKILL.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Code Generation
2+
3+
How the testify code and documentation generator works.
4+
5+
## Running
6+
7+
```bash
8+
# Generate everything (code + docs) from internal/assertions/
9+
go generate ./...
10+
11+
# Or run the generator directly
12+
cd codegen && go run . -output-packages assert,require -include-doc
13+
14+
# Code only (skip docs)
15+
cd codegen && go run . -output-packages assert,require -include-doc=false
16+
```
17+
18+
## Generator pipeline
19+
20+
1. **Scanner** (`codegen/internal/scanner/`) parses `internal/assertions/` using
21+
`go/packages` and `go/types`. Extracts function signatures, doc comments,
22+
domain tags, examples, and metadata.
23+
- `comments/` -- doc comment extraction
24+
- `comments-parser/` -- domain tags, examples, metadata parsing
25+
- `signature/` -- function signature analysis
26+
27+
2. **Model** (`codegen/internal/model/`) holds the intermediate representation:
28+
functions, type params, tests, documentation, metrics.
29+
30+
3. **Generator** (`codegen/internal/generator/`) renders templates:
31+
- **Code templates** produce `assert/` and `require/` packages with all variants
32+
- **Doc templates** produce Hugo markdown in `docs/doc-site/api/`
33+
- **Metrics** produces `metrics.yaml` for Hugo site params
34+
35+
## Key templates
36+
37+
Located in `codegen/internal/generator/templates/`:
38+
39+
| Template | Produces |
40+
|----------|----------|
41+
| `assertion_assertions.gotmpl` | `assert` package-level functions |
42+
| `assertion_format.gotmpl` | `assert` formatted variants (`*f`) |
43+
| `assertion_forward.gotmpl` | `assert` forward methods |
44+
| `requirement_*.gotmpl` | `require` equivalents (calls `FailNow`) |
45+
| `doc_index.md.gotmpl` | API index page (`_index.md`) |
46+
| `doc_page.md.gotmpl` | Per-domain doc pages |
47+
| `doc_metrics.md.gotmpl` | Quick index & metrics page |
48+
49+
## Template functions
50+
51+
Custom template functions in `codegen/internal/generator/funcmaps/`:
52+
- `Slugize(name)` -- converts function name to markdown anchor
53+
- `Titleize(s)` -- title-cases a string
54+
- `hopen` / `hclose` -- Hugo shortcode delimiters (`{{% ... %}}`)
55+
56+
## Domain organization
57+
58+
Functions are grouped by `// Domain: <name>` tags inside the function body.
59+
Domain descriptions live in `internal/assertions/doc.go` as special comments.
60+
The generator reorganizes package-based docs into domain-based pages
61+
(19 domains currently).
62+
63+
## Generated output
64+
65+
**Never edit generated files directly.** They carry a `DO NOT EDIT` header.
66+
67+
| Output | Location |
68+
|--------|----------|
69+
| `assert/` package | Generated functions + tests |
70+
| `require/` package | Generated functions + tests |
71+
| `docs/doc-site/api/*.md` | Domain-organized Hugo pages |
72+
| `docs/doc-site/api/metrics.md` | Quick index + API metrics |
73+
| `hack/doc-site/hugo/metrics.yaml` | Hugo site params (counts) |
74+
75+
Exceptions (not generated): `assert/doc.go`, `require/doc.go`, ad-hoc testable examples.
76+
77+
## Adding support for a new construct
78+
79+
If the generator needs to support a new Go construct (e.g., new type param
80+
pattern), the work is in:
81+
1. `codegen/internal/scanner/` -- teach the scanner to extract it
82+
2. `codegen/internal/model/` -- add fields to the model
83+
3. `codegen/internal/generator/templates/` -- update templates to render it
84+
85+
The scanner and generator have comprehensive tests (~1,400+ lines across test files).

.claude/skills/doc-site/SKILL.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Documentation Site
2+
3+
Hugo-based documentation site for testify, auto-generated from source code.
4+
5+
## Running locally
6+
7+
```bash
8+
# 1. Generate docs from source
9+
go generate ./...
10+
11+
# 2. Start Hugo dev server
12+
cd hack/doc-site/hugo
13+
./gendoc.sh
14+
15+
# Visit http://localhost:1313/testify/
16+
# Auto-reloads on changes to docs/doc-site/
17+
```
18+
19+
## Site structure
20+
21+
```
22+
hack/doc-site/hugo/
23+
hugo.yaml # Main Hugo config
24+
metrics.yaml # Generated metrics (codegen output, merged into site params)
25+
testify.yaml # Version info + build metadata
26+
gendoc.sh # Dev server launcher
27+
layouts/ # Custom layout overrides
28+
themes/hugo-relearn/ # Relearn documentation theme
29+
30+
docs/doc-site/ # Content (mounted by Hugo)
31+
api/ # Generated: domain pages, index, metrics (DO NOT EDIT)
32+
usage/ # Hand-written: USAGE, GENERICS, CHANGES, MIGRATION, etc.
33+
project/ # Hand-written: APPROACH, maintainer docs
34+
```
35+
36+
## Generated vs hand-written content
37+
38+
| Path | Generated? | Notes |
39+
|------|-----------|-------|
40+
| `docs/doc-site/api/*.md` | Yes | Domain pages, index, metrics. Regenerate with `go generate` |
41+
| `docs/doc-site/api/metrics.md` | Yes | Quick index table + API counts |
42+
| `docs/doc-site/usage/*.md` | No | Hand-written guides |
43+
| `docs/doc-site/project/*.md` | No | Hand-written project docs |
44+
45+
## Dynamic counts via Hugo params
46+
47+
Function and assertion counts are generated into `metrics.yaml` and merged
48+
into Hugo's `site.Params.metrics`. Use the relearn `siteparam` shortcode
49+
to reference them in hand-written markdown:
50+
51+
```markdown
52+
We have {{% siteparam "metrics.assertions" %}} assertions across
53+
{{% siteparam "metrics.domains" %}} domains.
54+
```
55+
56+
Available params: `metrics.domains`, `metrics.functions`, `metrics.assertions`,
57+
`metrics.generics`, `metrics.nongeneric_assertions`, `metrics.helpers`, `metrics.others`.
58+
59+
Per-domain: `metrics.by_domain.<slug>.count`, `metrics.by_domain.<slug>.name`.
60+
61+
Hugo math functions (`sub`, `mul`, `add`) are NOT available in markdown content.
62+
For computed values, add them to the codegen `buildMetrics()` in
63+
`codegen/internal/generator/doc_generator.go`.
64+
65+
## Adding a new hand-written page
66+
67+
1. Create `docs/doc-site/<section>/<FILE>.md` with Hugo front matter
68+
2. Set `weight:` to control ordering in the sidebar
69+
3. Use relearn shortcodes: `{{% notice %}}`, `{{% expand %}}`, `{{< tabs >}}`, etc.
70+
4. Reference API counts with `{{% siteparam "metrics.<key>" %}}`
71+
72+
## Relearn theme features used
73+
74+
- `{{% notice style="info" %}}` -- callout boxes
75+
- `{{% expand title="..." %}}` -- collapsible sections
76+
- `{{< tabs >}}` / `{{% tab %}}` -- tabbed content
77+
- `{{< cards >}}` / `{{% card %}}` -- side-by-side cards
78+
- `{{% icon icon="star" color=orange %}}` -- inline icons
79+
- `{{% siteparam "key" %}}` -- site param substitution
80+
- `{{< mermaid >}}` -- diagrams

0 commit comments

Comments
 (0)