@softspark/ai-toolkit 2.11.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +21 -21
- package/CHANGELOG.md +62 -0
- package/README.md +16 -9
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/agents/meta-architect.md +44 -0
- package/app/skills/agent-creator/SKILL.md +22 -6
- package/app/skills/analyze/SKILL.md +21 -1
- package/app/skills/api-patterns/SKILL.md +27 -0
- package/app/skills/app-builder/SKILL.md +24 -0
- package/app/skills/architecture-audit/SKILL.md +24 -0
- package/app/skills/architecture-decision/SKILL.md +24 -0
- package/app/skills/biz-scan/SKILL.md +24 -0
- package/app/skills/brand-voice/SKILL.md +36 -0
- package/app/skills/briefing/SKILL.md +41 -1
- package/app/skills/build/SKILL.md +24 -1
- package/app/skills/chaos/SKILL.md +22 -1
- package/app/skills/ci/SKILL.md +22 -1
- package/app/skills/ci-cd-patterns/SKILL.md +25 -0
- package/app/skills/command-creator/SKILL.md +31 -2
- package/app/skills/content-moderation-patterns/SKILL.md +25 -0
- package/app/skills/database-patterns/SKILL.md +25 -0
- package/app/skills/debug/SKILL.md +23 -1
- package/app/skills/design-engineering/SKILL.md +25 -0
- package/app/skills/docker-devops/SKILL.md +26 -0
- package/app/skills/ecommerce-patterns/SKILL.md +26 -0
- package/app/skills/evaluate/SKILL.md +22 -1
- package/app/skills/evolve/SKILL.md +25 -0
- package/app/skills/explain/SKILL.md +26 -1
- package/app/skills/explore/SKILL.md +25 -0
- package/app/skills/fix/SKILL.md +26 -1
- package/app/skills/git-mastery/SKILL.md +26 -0
- package/app/skills/grill-me/SKILL.md +22 -7
- package/app/skills/health/SKILL.md +22 -1
- package/app/skills/hipaa-validate/SKILL.md +25 -8
- package/app/skills/hook-creator/SKILL.md +107 -14
- package/app/skills/index/SKILL.md +22 -1
- package/app/skills/instinct-review/SKILL.md +25 -1
- package/app/skills/introspect/SKILL.md +26 -1
- package/app/skills/lint/SKILL.md +27 -1
- package/app/skills/mcp-builder/SKILL.md +27 -0
- package/app/skills/mem-search/SKILL.md +40 -0
- package/app/skills/migrate/SKILL.md +27 -1
- package/app/skills/migration-patterns/SKILL.md +26 -0
- package/app/skills/night-watch/SKILL.md +22 -1
- package/app/skills/observability-patterns/SKILL.md +26 -0
- package/app/skills/onboard/SKILL.md +22 -1
- package/app/skills/panic/SKILL.md +21 -1
- package/app/skills/performance-profiling/SKILL.md +34 -0
- package/app/skills/persona/SKILL.md +21 -4
- package/app/skills/plan/SKILL.md +26 -0
- package/app/skills/plan-writing/SKILL.md +26 -0
- package/app/skills/plugin-creator/SKILL.md +23 -6
- package/app/skills/pr/SKILL.md +24 -1
- package/app/skills/prd-to-issues/SKILL.md +22 -6
- package/app/skills/prd-to-plan/SKILL.md +22 -4
- package/app/skills/predict/SKILL.md +25 -0
- package/app/skills/qa-session/SKILL.md +23 -7
- package/app/skills/rag-patterns/SKILL.md +26 -0
- package/app/skills/refactor/SKILL.md +26 -0
- package/app/skills/refactor-plan/SKILL.md +23 -6
- package/app/skills/rollback/SKILL.md +27 -1
- package/app/skills/security-patterns/SKILL.md +27 -0
- package/app/skills/skill-audit/SKILL.md +22 -5
- package/app/skills/skill-creator/SKILL.md +58 -13
- package/app/skills/swift-patterns/SKILL.md +27 -107
- package/app/skills/swift-patterns/reference/frameworks.md +111 -0
- package/app/skills/test/SKILL.md +23 -1
- package/app/skills/testing-patterns/SKILL.md +26 -0
- package/app/skills/triage-issue/SKILL.md +22 -4
- package/app/skills/ubiquitous-language/SKILL.md +23 -8
- package/benchmarks/ecosystem-doctor-snapshot.json +395 -0
- package/kb/history/completed/deep-coverage-v3-20260423.md +160 -0
- package/kb/history/completed/ecosystem-deep-sweep-20260423.md +273 -0
- package/kb/procedures/ecosystem-sync-sop.md +255 -0
- package/kb/procedures/maintenance-sop.md +13 -2
- package/kb/procedures/release-preparation-sop.md +91 -12
- package/kb/procedures/release-verification-sop.md +24 -6
- package/kb/reference/global-install-model.md +15 -2
- package/kb/reference/supported-tools-registry.md +229 -0
- package/llms-full.txt +1105 -43
- package/llms.txt +4 -0
- package/manifest.json +1 -1
- package/package.json +4 -1
- package/scripts/ecosystem_doctor.py +348 -0
- package/scripts/ecosystem_tools.json +500 -0
- package/scripts/evaluate_skills.py +84 -0
- package/scripts/generate_aider_conf.py +26 -1
- package/scripts/generate_antigravity.py +77 -8
- package/scripts/generate_augment_agents.py +161 -0
- package/scripts/generate_augment_commands.py +160 -0
- package/scripts/generate_augment_hooks.py +162 -0
- package/scripts/generate_augment_skills.py +98 -0
- package/scripts/generate_cline_rules.py +96 -9
- package/scripts/generate_codex_hooks.py +13 -2
- package/scripts/generate_codex_skills.py +195 -0
- package/scripts/generate_copilot.py +296 -18
- package/scripts/generate_cursor_agents.py +144 -0
- package/scripts/generate_cursor_hooks.py +155 -0
- package/scripts/generate_cursor_mdc.py +20 -8
- package/scripts/generate_gemini_commands.py +158 -0
- package/scripts/generate_gemini_hooks.py +159 -0
- package/scripts/generate_gemini_skills.py +98 -0
- package/scripts/generate_roo_modes.py +42 -1
- package/scripts/generate_windsurf_hooks.py +143 -0
- package/scripts/generate_windsurf_rules.py +162 -10
- package/scripts/install.py +11 -2
- package/scripts/install_steps/ai_tools.py +120 -5
- package/scripts/validate.py +20 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rollback
|
|
3
|
-
description: "Roll back a deployment
|
|
3
|
+
description: "Roll back a git commit, database migration, or deployment to a previous known-good state with safety checks and health verification. Use when the user wants to revert recent changes safely — not to undo local edits or halt the whole system."
|
|
4
4
|
effort: medium
|
|
5
5
|
disable-model-invocation: true
|
|
6
6
|
argument-hint: "[target: git/db/deploy]"
|
|
@@ -85,3 +85,29 @@ Before any rollback:
|
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
> **CRITICAL: Always confirm with the user before executing destructive rollback operations.**
|
|
88
|
+
|
|
89
|
+
## Rules
|
|
90
|
+
|
|
91
|
+
- **MUST** confirm the current state AND the target state before rolling back — surface the diff in plain English
|
|
92
|
+
- **MUST** verify a recent backup exists (for DB rollbacks) or explicitly warn the user that none was found
|
|
93
|
+
- **NEVER** roll back without an explicit "yes" from the user — rollbacks are irreversible in the user-experience sense even when technically reversible
|
|
94
|
+
- **NEVER** `git reset --hard` on a branch others have pulled from — it rewrites shared history
|
|
95
|
+
- **CRITICAL**: after any rollback, run a health check (`/health` or the project's equivalent) to confirm the target state is stable — a "successful" rollback to a broken baseline is worse than the original state
|
|
96
|
+
- **MANDATORY**: log the rollback with timestamp, scope, and reason — post-mortems need this trail
|
|
97
|
+
|
|
98
|
+
## Gotchas
|
|
99
|
+
|
|
100
|
+
- `git revert` creates a **new commit** that undoes the target commit. The reverted commit is still in history — if the target commit was sensitive (secret, PII), revert alone does not remove it. Use history rewriting tools for that.
|
|
101
|
+
- Database migration rollbacks sometimes **lose data**. A forward migration that added a NOT NULL column with a default, then populated it with user data, cannot restore the column contents on rollback — the data is gone.
|
|
102
|
+
- Kubernetes `kubectl rollout undo` rolls back to the previous ReplicaSet, not to a specific version. If you need "rollback to v1.2.3 specifically", track deployments by image tag and use `kubectl set image`, not `rollout undo`.
|
|
103
|
+
- Heroku and similar PaaS platforms `rollback` restores the slug but not environment config that changed after the rollback target was built — new env vars or add-ons may break the rolled-back version.
|
|
104
|
+
- `git reset --soft` preserves staged changes, `--mixed` (default) preserves working tree, `--hard` discards both. Wrong flag = lost work; always state the flag explicitly in the confirmation prompt.
|
|
105
|
+
- Restoring a DB backup on top of an active database can cause data loss between the backup time and the restore time. Take a fresh snapshot before the restore, even when rolling back — the current (broken) state might contain post-backup user writes.
|
|
106
|
+
|
|
107
|
+
## When NOT to Use
|
|
108
|
+
|
|
109
|
+
- For emergency halt of all agent activity — use `/panic`
|
|
110
|
+
- For undoing local uncommitted edits — use `git checkout` directly, not this skill
|
|
111
|
+
- For an incident with user-facing impact — use `/workflow incident-response` for coordinated response
|
|
112
|
+
- For planned schema changes — use `/migrate` with a forward rollback migration, not this skill after-the-fact
|
|
113
|
+
- For a feature flag off-switch — toggle the flag; rollback is a heavier tool than needed
|
|
@@ -100,3 +100,30 @@ For authorization patterns (RBAC, ABAC), see [reference/authorization.md](refere
|
|
|
100
100
|
For input validation patterns (SQL injection, XSS, Pydantic), see [reference/input-validation.md](reference/input-validation.md).
|
|
101
101
|
|
|
102
102
|
For OAuth2 flows, CSRF protection, and audit logging, see [reference/oauth-csrf-audit.md](reference/oauth-csrf-audit.md).
|
|
103
|
+
|
|
104
|
+
## Rules
|
|
105
|
+
|
|
106
|
+
- **MUST** validate all input at the trust boundary, not inside business logic — deep validation allows bad data to spread before rejection
|
|
107
|
+
- **MUST** use parameterized queries (prepared statements) for every SQL interaction — string concatenation is SQL injection
|
|
108
|
+
- **NEVER** store secrets (API keys, tokens, passwords) in code, config files, or git history — use the platform's secret manager
|
|
109
|
+
- **NEVER** log passwords, tokens, PII, or PHI — even at debug level. Logs reach aggregation systems, backups, and disk snapshots.
|
|
110
|
+
- **NEVER** roll your own crypto. Use vetted libraries (bcrypt/argon2 for passwords, libsodium for crypto) and accept their defaults.
|
|
111
|
+
- **CRITICAL**: authentication (who you are) and authorization (what you can do) are distinct concerns. Confusing them produces privilege escalation bugs; AuthN passes → AuthZ still runs.
|
|
112
|
+
- **MANDATORY**: every endpoint is authenticated and authorized by default. Public endpoints are explicit opt-outs, not unmarked defaults.
|
|
113
|
+
|
|
114
|
+
## Gotchas
|
|
115
|
+
|
|
116
|
+
- JWT tokens signed with `none` algorithm are valid-looking tokens with no signature. Libraries that trust the `alg` header field accept them — always validate `alg` against an allowlist, never use the token's own declaration.
|
|
117
|
+
- `bcrypt` has a 72-byte password length cap; longer passwords are silently truncated, making "UniqueLongPassword..." collide with "UniqueLong...". Pre-hash with SHA-256 before bcrypt for >72 char passwords.
|
|
118
|
+
- `SameSite=Lax` cookies are **sent** on top-level navigations (including POST from a malicious site) in some browsers. CSRF protection requires either `SameSite=Strict` or explicit CSRF tokens; relying on `Lax` alone is insufficient for state-changing endpoints.
|
|
119
|
+
- `Content-Security-Policy` with `unsafe-inline` allows any inline script to run — negating most of CSP's value. Remove `unsafe-inline` and refactor to external scripts, even if it means extra files.
|
|
120
|
+
- Environment variables leak via `printenv` in debug endpoints, `/proc/<pid>/environ` on Linux, and process listings. Prefer mounting secrets as files (Docker secrets, Kubernetes secrets) for defense in depth.
|
|
121
|
+
- Rate limiting by IP address is bypassed by CDN proxies and legitimate shared NAT. Apply rate limits at the **authenticated user** level when possible; IP-level is a coarse last resort.
|
|
122
|
+
|
|
123
|
+
## When NOT to Load
|
|
124
|
+
|
|
125
|
+
- For dependency vulnerability scanning — use `/cve-scan`
|
|
126
|
+
- For HIPAA-specific healthcare compliance — use `/hipaa-validate`
|
|
127
|
+
- For threat modeling of a new architecture — delegate to the `security-architect` agent
|
|
128
|
+
- For penetration testing and CVE exploitation — delegate to `security-auditor` agent
|
|
129
|
+
- For content moderation (LLM safety filters) — use `/content-moderation-patterns`
|
|
@@ -130,8 +130,25 @@ The `/skill-audit` slash command wraps this scanner with Claude's analysis for r
|
|
|
130
130
|
|
|
131
131
|
## Rules
|
|
132
132
|
|
|
133
|
-
-
|
|
134
|
-
- HIGH
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
133
|
+
- **MUST** remain read-only by default — file modifications require the explicit `--fix` flag
|
|
134
|
+
- **MUST** exit with non-zero status on any HIGH finding so CI pipelines can gate merges
|
|
135
|
+
- **NEVER** auto-fix HIGH-severity findings — only the human owner decides on dangerous code
|
|
136
|
+
- **NEVER** silence findings by adding exceptions in the audit config; either fix the code or document why the pattern is safe in the skill body
|
|
137
|
+
- **CRITICAL**: scan both `app/skills/` and `app/agents/` — agents without tool restrictions are the same risk class as skills with broad `allowed-tools`
|
|
138
|
+
- **MANDATORY**: every finding names a specific fix (replace `eval()` with `ast.literal_eval()`, add missing `allowed-tools`). A finding without a fix is triage noise.
|
|
139
|
+
|
|
140
|
+
## Gotchas
|
|
141
|
+
|
|
142
|
+
- Regex-based secret detection catches canonical patterns (`sk-...`, `ghp_...`) but misses custom API key formats used by internal services. Augment the regex list with project-specific patterns before trusting "0 HIGH findings".
|
|
143
|
+
- `--fix` on `allowed-tools` infers minimal tool sets from imports, but skills that shell out via Bash may need tools not visible in the static scan. Review auto-added restrictions before merging.
|
|
144
|
+
- Knowledge skills (`user-invocable: false`) with Bash access are HIGH because they auto-load and can act without user triggering. Legitimate exceptions (e.g., `research-mastery` calling `smart_query()`) should be explicitly whitelisted in the audit config with a comment.
|
|
145
|
+
- The scanner flags `eval(` even inside docstrings and commented-out code. Context-aware scanning is hard; the alternative is reviewing each HIGH flag manually — the scan errs on the side of false positives.
|
|
146
|
+
- CI integration with `--ci` exits 1 on any HIGH, which **blocks the commit**. A sudden pattern match (e.g., a legitimate new use of `subprocess.run`) can block unrelated PRs. Keep a fast path for pre-approving new patterns.
|
|
147
|
+
|
|
148
|
+
## When NOT to Use
|
|
149
|
+
|
|
150
|
+
- For general code-quality metrics (complexity, coverage, duplication) — use `/analyze`
|
|
151
|
+
- For dependency CVE scans — use `/cve-scan`
|
|
152
|
+
- For HIPAA-specific audits — use `/hipaa-validate`
|
|
153
|
+
- For live pentesting of a deployed app — delegate to the `security-auditor` agent
|
|
154
|
+
- When the project has its own security scanner (semgrep, snyk) — prefer it; `/skill-audit` is toolkit-specific
|
|
@@ -28,15 +28,19 @@ Create a new skill following the Agent Skills standard.
|
|
|
28
28
|
|-------|------|----------|-------------|
|
|
29
29
|
| `name` | string | yes | Lowercase, hyphens only, max 64 chars |
|
|
30
30
|
| `description` | string | yes | Third person, max 1024 chars, include key terms |
|
|
31
|
-
| `effort` | low/medium/high/max | no | Controls model thinking budget |
|
|
31
|
+
| `effort` | low/medium/high/xhigh/max | no | Controls model thinking budget (xhigh added for Opus 4.7) |
|
|
32
32
|
| `disable-model-invocation` | bool | no | `true` = only user can trigger (task skills) |
|
|
33
33
|
| `user-invocable` | bool | no | `false` = knowledge skill, Claude auto-loads |
|
|
34
|
-
| `allowed-tools` | csv | no | Restrict tool access for safety |
|
|
35
|
-
| `
|
|
34
|
+
| `allowed-tools` | csv or YAML list | no | Restrict tool access for safety; YAML-style lists accepted |
|
|
35
|
+
| `disallowedTools` | csv or YAML list | no | Block specific tools (plugin-shipped agents) |
|
|
36
|
+
| `model` | string | no | Override default model (accepts full IDs like `claude-opus-4-7`) |
|
|
36
37
|
| `context` | string | no | `fork` to run in isolated subagent |
|
|
37
38
|
| `agent` | string | no | Agent type to use when `context: fork` |
|
|
39
|
+
| `skills` | csv | no | Auto-load skills for the invoked subagent |
|
|
38
40
|
| `argument-hint` | string | no | Shown in autocomplete, e.g. `"[target]"` |
|
|
39
|
-
| `hooks` | object | no | Lifecycle hooks (PreToolUse
|
|
41
|
+
| `hooks` | object | no | Lifecycle hooks (`PreToolUse`, `PostToolUse`, `Stop`) scoped to the skill invocation |
|
|
42
|
+
| `maxTurns` | int | no | Cap turns when skill spawns a subagent |
|
|
43
|
+
| `memory` | user/project/local | no | Persistent memory scope (agents only) |
|
|
40
44
|
|
|
41
45
|
## Classification Guide
|
|
42
46
|
|
|
@@ -48,7 +52,7 @@ Create a new skill following the Agent Skills standard.
|
|
|
48
52
|
|
|
49
53
|
## Writing Guidelines
|
|
50
54
|
|
|
51
|
-
- **Description**: third person ("Generates...", "Provides..."), include searchable key terms
|
|
55
|
+
- **Description**: third person ("Generates...", "Provides..."), **min 50 chars**, include searchable key terms **and a trigger hint** (e.g., "Use when..."). Short descriptions cause over-triggering — adjacent skills fight for the same query.
|
|
52
56
|
- **Name**: lowercase, hyphens, max 64 chars -- match the directory name
|
|
53
57
|
- **Length**: SKILL.md under 500 lines; use `reference/` for overflow
|
|
54
58
|
- **Be concise**: Claude is smart -- give structure, not lectures
|
|
@@ -57,6 +61,26 @@ Create a new skill following the Agent Skills standard.
|
|
|
57
61
|
- **Use `$ARGUMENTS`**: place it early so user input is visible
|
|
58
62
|
- **Tables over prose**: for options, patterns, mappings
|
|
59
63
|
|
|
64
|
+
## Mandatory Sections (meta-architect audit criteria)
|
|
65
|
+
|
|
66
|
+
Every SKILL.md must contain all five, or it will lower the toolkit quality score:
|
|
67
|
+
|
|
68
|
+
1. **Description ≥50 chars** with trigger hint in frontmatter
|
|
69
|
+
2. **At least one concrete example** — fenced code block or `## Example` section
|
|
70
|
+
3. **Hard rules** using uppercase markers: `MUST`, `NEVER`, `CRITICAL`, `MANDATORY`, or `DO NOT`
|
|
71
|
+
4. **"When NOT to Use" section** — list 2-3 adjacent skills and the boundary between them
|
|
72
|
+
5. **Under 500 lines** — split into `reference/` if needed
|
|
73
|
+
|
|
74
|
+
These map directly to the mutation strategies in `meta-architect.md`:
|
|
75
|
+
`add_example`, `add_constraint`, `add_edge_case`, `restructure`.
|
|
76
|
+
|
|
77
|
+
### Rules vs Gotchas — different semantic buckets
|
|
78
|
+
|
|
79
|
+
Split the "hard rules" criterion into two sections when both apply:
|
|
80
|
+
|
|
81
|
+
- **`## Rules`** — prescriptive process constraints (always-true MUST / NEVER): *"MUST form a testable hypothesis before changing code"*, *"NEVER force-push main"*. Required in every skill.
|
|
82
|
+
- **`## Gotchas`** — environment-specific traps the agent would miss without being told, taken from [Anthropic's best practices](https://agentskills.io/skill-creation/best-practices.md#gotchas-sections). Example from their docs: *"The `users` table uses soft deletes. Queries must include `WHERE deleted_at IS NULL` or results will include deactivated accounts."* Add this section only when real domain traps exist — do not pad with a `(none)` placeholder.
|
|
83
|
+
|
|
60
84
|
## Directory Structure
|
|
61
85
|
|
|
62
86
|
```
|
|
@@ -74,7 +98,7 @@ Only create subdirectories when the skill needs them. Most skills are a single S
|
|
|
74
98
|
```markdown
|
|
75
99
|
---
|
|
76
100
|
name: {name}
|
|
77
|
-
description: "{Third-person description with
|
|
101
|
+
description: "{Third-person description, min 50 chars, with trigger hint like 'Use when...'}"
|
|
78
102
|
argument-hint: "[hint]"
|
|
79
103
|
allowed-tools: Read, Grep, Glob
|
|
80
104
|
---
|
|
@@ -97,25 +121,46 @@ $ARGUMENTS
|
|
|
97
121
|
2. **Step two**
|
|
98
122
|
3. **Step three**
|
|
99
123
|
|
|
100
|
-
##
|
|
124
|
+
## Example
|
|
101
125
|
|
|
102
|
-
|
|
126
|
+
\`\`\`
|
|
127
|
+
/{name} example-argument
|
|
128
|
+
\`\`\`
|
|
129
|
+
|
|
130
|
+
{Expected observable behavior.}
|
|
103
131
|
|
|
104
132
|
## Rules
|
|
105
133
|
|
|
106
|
-
- {
|
|
107
|
-
- {
|
|
134
|
+
- **MUST** {non-negotiable rule 1}
|
|
135
|
+
- **NEVER** {forbidden action}
|
|
136
|
+
- **CRITICAL**: {safety constraint}
|
|
137
|
+
|
|
138
|
+
## Gotchas
|
|
139
|
+
|
|
140
|
+
- {environment-specific trap the agent would miss — concrete, not general}
|
|
141
|
+
- {non-obvious behavior of a tool, API, or data layout}
|
|
142
|
+
|
|
143
|
+
## When NOT to Use
|
|
144
|
+
|
|
145
|
+
- For {adjacent use case} -- use `/{other-skill}` instead
|
|
146
|
+
- For {another case} -- use `/{another-skill}`
|
|
147
|
+
- If {precondition} is not met
|
|
108
148
|
```
|
|
109
149
|
|
|
150
|
+
Leave out `## Gotchas` entirely when the skill has no domain-specific traps — it is not mandatory, and a stub with nothing concrete is worse than no section.
|
|
151
|
+
|
|
110
152
|
## Quality Checklist
|
|
111
153
|
|
|
112
154
|
Before finalizing, verify:
|
|
113
155
|
|
|
114
|
-
- [ ] Description
|
|
115
|
-
- [ ]
|
|
156
|
+
- [ ] Description ≥50 chars, third-person, with trigger hint
|
|
157
|
+
- [ ] At least one concrete code-fenced example
|
|
158
|
+
- [ ] `## Rules` section with prescriptive MUST / NEVER / CRITICAL / MANDATORY
|
|
159
|
+
- [ ] `## Gotchas` section when the domain has real environment-specific traps (otherwise omit)
|
|
160
|
+
- [ ] `## When NOT to Use` section naming 2-3 adjacent skills
|
|
161
|
+
- [ ] SKILL.md under 500 lines
|
|
116
162
|
- [ ] No time-sensitive information (versions, dates)
|
|
117
163
|
- [ ] Consistent terminology throughout
|
|
118
|
-
- [ ] Examples are concrete, not abstract
|
|
119
164
|
- [ ] File references max 1 level deep
|
|
120
165
|
- [ ] Workflows have numbered steps
|
|
121
166
|
- [ ] Frontmatter fields match classification type
|
|
@@ -280,113 +280,7 @@ func testLoginFlow() {
|
|
|
280
280
|
|
|
281
281
|
## Common Frameworks
|
|
282
282
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
```swift
|
|
286
|
-
@Observable
|
|
287
|
-
final class UserViewModel {
|
|
288
|
-
var users: [User] = []
|
|
289
|
-
var isLoading = false
|
|
290
|
-
private let service: UserService
|
|
291
|
-
|
|
292
|
-
init(service: UserService) { self.service = service }
|
|
293
|
-
|
|
294
|
-
func load() async {
|
|
295
|
-
isLoading = true
|
|
296
|
-
defer { isLoading = false }
|
|
297
|
-
users = (try? await service.fetchAll()) ?? []
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
struct UserListView: View {
|
|
302
|
-
@State private var vm: UserViewModel
|
|
303
|
-
|
|
304
|
-
init(service: UserService) {
|
|
305
|
-
_vm = State(initialValue: UserViewModel(service: service))
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
var body: some View {
|
|
309
|
-
NavigationStack {
|
|
310
|
-
List(vm.users) { user in
|
|
311
|
-
NavigationLink(value: user) { Text(user.name) }
|
|
312
|
-
}
|
|
313
|
-
.navigationTitle("Users")
|
|
314
|
-
.navigationDestination(for: User.self) { UserDetailView(user: $0) }
|
|
315
|
-
.task { await vm.load() }
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### Combine
|
|
322
|
-
|
|
323
|
-
```swift
|
|
324
|
-
class SearchVM: ObservableObject {
|
|
325
|
-
@Published var query = ""
|
|
326
|
-
@Published var results: [Item] = []
|
|
327
|
-
private var cancellables = Set<AnyCancellable>()
|
|
328
|
-
|
|
329
|
-
init(service: SearchService) {
|
|
330
|
-
$query
|
|
331
|
-
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
|
|
332
|
-
.removeDuplicates()
|
|
333
|
-
.filter { !$0.isEmpty }
|
|
334
|
-
.flatMap { service.search(query: $0) }
|
|
335
|
-
.receive(on: DispatchQueue.main)
|
|
336
|
-
.sink(receiveCompletion: { _ in },
|
|
337
|
-
receiveValue: { [weak self] in self?.results = $0 })
|
|
338
|
-
.store(in: &cancellables)
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
### Structured Concurrency
|
|
344
|
-
|
|
345
|
-
```swift
|
|
346
|
-
func fetchAllProfiles(ids: [String]) async throws -> [Profile] {
|
|
347
|
-
try await withThrowingTaskGroup(of: Profile.self) { group in
|
|
348
|
-
for id in ids { group.addTask { try await fetchProfile(id: id) } }
|
|
349
|
-
return try await group.reduce(into: []) { $0.append($1) }
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// AsyncStream for bridging callbacks
|
|
354
|
-
let locations = AsyncStream<Location> { continuation in
|
|
355
|
-
manager.onUpdate = { continuation.yield($0) }
|
|
356
|
-
continuation.onTermination = { _ in manager.stop() }
|
|
357
|
-
}
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
### SwiftData
|
|
361
|
-
|
|
362
|
-
```swift
|
|
363
|
-
@Model
|
|
364
|
-
final class Item {
|
|
365
|
-
var title: String
|
|
366
|
-
var timestamp: Date
|
|
367
|
-
@Relationship(deleteRule: .cascade) var tags: [Tag]
|
|
368
|
-
init(title: String) { self.title = title; self.timestamp = .now; self.tags = [] }
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
struct ItemListView: View {
|
|
372
|
-
@Query(sort: \Item.timestamp, order: .reverse) private var items: [Item]
|
|
373
|
-
@Environment(\.modelContext) private var context
|
|
374
|
-
|
|
375
|
-
var body: some View {
|
|
376
|
-
List(items) { Text($0.title) }
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### Vapor (Server-Side)
|
|
382
|
-
|
|
383
|
-
```swift
|
|
384
|
-
app.get("users", ":id") { req async throws -> User in
|
|
385
|
-
guard let id = req.parameters.get("id", as: UUID.self) else { throw Abort(.badRequest) }
|
|
386
|
-
guard let user = try await User.find(id, on: req.db) else { throw Abort(.notFound) }
|
|
387
|
-
return user
|
|
388
|
-
}
|
|
389
|
-
```
|
|
283
|
+
For SwiftUI + `@Observable`, Combine, Structured Concurrency, SwiftData, and Vapor framework patterns with complete code examples, see [reference/frameworks.md](reference/frameworks.md).
|
|
390
284
|
|
|
391
285
|
---
|
|
392
286
|
|
|
@@ -498,3 +392,29 @@ Schemes: separate Debug/Release/Testing. Enable ASan + TSan in test schemes.
|
|
|
498
392
|
| Blocking main thread | UI freezes | `async/await`, `Task { }` |
|
|
499
393
|
| `UserDefaults` for secrets | Insecure | Keychain (`SecItemAdd`) |
|
|
500
394
|
| `@ObservedObject` for owned state | Object recreated | `@StateObject` or `@State` + `@Observable` |
|
|
395
|
+
|
|
396
|
+
## Rules
|
|
397
|
+
|
|
398
|
+
- **MUST** use Swift concurrency primitives (`async/await`, actors, `Task`) for new code — GCD is legacy and mixes poorly with the new model
|
|
399
|
+
- **MUST** annotate view models with `@MainActor` when they touch UI state — off-main mutations cause runtime warnings and flaky UI
|
|
400
|
+
- **NEVER** force-unwrap (`!`) without a documented invariant in a comment; runtime crashes from unwrap are the top iOS crash category
|
|
401
|
+
- **NEVER** store secrets in `UserDefaults` or plist — use Keychain APIs (`SecItemAdd`, `SecItemCopyMatching`)
|
|
402
|
+
- **CRITICAL**: SwiftUI state flows downward; mutations flow through `@State`, `@Binding`, or `@Observable`. Never mutate a parent's state from a child via a captured reference — it breaks dependency tracking.
|
|
403
|
+
- **MANDATORY**: every closure that captures `self` inside a reference type uses `[weak self]` or `[unowned self]` — retain cycles are the top memory-leak cause
|
|
404
|
+
|
|
405
|
+
## Gotchas
|
|
406
|
+
|
|
407
|
+
- `@StateObject` and `@ObservedObject` look similar but behave oppositely on parent re-render: `@StateObject` persists, `@ObservedObject` may re-initialize. Using `@ObservedObject` for view-owned state recreates the object on every render — state loss without error.
|
|
408
|
+
- `Task { @MainActor in ... }` inside a non-`@MainActor` context does **not** synchronously return to main; it schedules. Code between the `await` and `Task` boundary runs on whatever actor you came from, which can race with UI updates.
|
|
409
|
+
- `AsyncStream` continuations without `onTermination` leak: if the consumer cancels, the producer keeps yielding forever. Always install a termination handler.
|
|
410
|
+
- SwiftData `@Query` with `@Environment(\.modelContext)` invalidates on every write; heavy reads in a watched view cause perf drops. Use `@FetchRequest`-style fetch descriptors with explicit refresh, not ambient `@Query`, for large datasets.
|
|
411
|
+
- Combine's `.receive(on: DispatchQueue.main)` schedules asynchronously — if the next operator expects sync execution, order matters. Prefer moving `.receive(on:)` to just before the sink, not mid-pipeline.
|
|
412
|
+
- Swift Concurrency does not compose cleanly with Objective-C completion handlers; `withCheckedContinuation` bridges but a continuation that is never resumed hangs the Task forever. Always pair resumes with all control-flow paths, including errors.
|
|
413
|
+
|
|
414
|
+
## When NOT to Load
|
|
415
|
+
|
|
416
|
+
- For **Flutter or React Native** cross-platform code — use `/flutter-patterns` or JS patterns; this skill is Swift-only
|
|
417
|
+
- For generic iOS architecture decisions (MVC vs MVVM vs VIPER) — use `/architecture-decision`
|
|
418
|
+
- For Kotlin-based cross-platform mobile (KMP) — use `/kotlin-patterns`
|
|
419
|
+
- For mobile CI/CD specifics (TestFlight, Fastlane) — use `/ci-cd-patterns`
|
|
420
|
+
- For Objective-C interop deep dives — outside scope; this skill focuses on modern Swift
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Swift Framework Patterns
|
|
2
|
+
|
|
3
|
+
Detailed patterns for SwiftUI, Combine, Structured Concurrency, SwiftData, and Vapor. Loaded on demand from `swift-patterns/SKILL.md`.
|
|
4
|
+
|
|
5
|
+
## SwiftUI + @Observable (iOS 17+)
|
|
6
|
+
|
|
7
|
+
```swift
|
|
8
|
+
@Observable
|
|
9
|
+
final class UserViewModel {
|
|
10
|
+
var users: [User] = []
|
|
11
|
+
var isLoading = false
|
|
12
|
+
private let service: UserService
|
|
13
|
+
|
|
14
|
+
init(service: UserService) { self.service = service }
|
|
15
|
+
|
|
16
|
+
func load() async {
|
|
17
|
+
isLoading = true
|
|
18
|
+
defer { isLoading = false }
|
|
19
|
+
users = (try? await service.fetchAll()) ?? []
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
struct UserListView: View {
|
|
24
|
+
@State private var vm: UserViewModel
|
|
25
|
+
|
|
26
|
+
init(service: UserService) {
|
|
27
|
+
_vm = State(initialValue: UserViewModel(service: service))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var body: some View {
|
|
31
|
+
NavigationStack {
|
|
32
|
+
List(vm.users) { user in
|
|
33
|
+
NavigationLink(value: user) { Text(user.name) }
|
|
34
|
+
}
|
|
35
|
+
.navigationTitle("Users")
|
|
36
|
+
.navigationDestination(for: User.self) { UserDetailView(user: $0) }
|
|
37
|
+
.task { await vm.load() }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Combine
|
|
44
|
+
|
|
45
|
+
```swift
|
|
46
|
+
class SearchVM: ObservableObject {
|
|
47
|
+
@Published var query = ""
|
|
48
|
+
@Published var results: [Item] = []
|
|
49
|
+
private var cancellables = Set<AnyCancellable>()
|
|
50
|
+
|
|
51
|
+
init(service: SearchService) {
|
|
52
|
+
$query
|
|
53
|
+
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
|
|
54
|
+
.removeDuplicates()
|
|
55
|
+
.filter { !$0.isEmpty }
|
|
56
|
+
.flatMap { service.search(query: $0) }
|
|
57
|
+
.receive(on: DispatchQueue.main)
|
|
58
|
+
.sink(receiveCompletion: { _ in },
|
|
59
|
+
receiveValue: { [weak self] in self?.results = $0 })
|
|
60
|
+
.store(in: &cancellables)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Structured Concurrency
|
|
66
|
+
|
|
67
|
+
```swift
|
|
68
|
+
func fetchAllProfiles(ids: [String]) async throws -> [Profile] {
|
|
69
|
+
try await withThrowingTaskGroup(of: Profile.self) { group in
|
|
70
|
+
for id in ids { group.addTask { try await fetchProfile(id: id) } }
|
|
71
|
+
return try await group.reduce(into: []) { $0.append($1) }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// AsyncStream for bridging callbacks
|
|
76
|
+
let locations = AsyncStream<Location> { continuation in
|
|
77
|
+
manager.onUpdate = { continuation.yield($0) }
|
|
78
|
+
continuation.onTermination = { _ in manager.stop() }
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## SwiftData
|
|
83
|
+
|
|
84
|
+
```swift
|
|
85
|
+
@Model
|
|
86
|
+
final class Item {
|
|
87
|
+
var title: String
|
|
88
|
+
var timestamp: Date
|
|
89
|
+
@Relationship(deleteRule: .cascade) var tags: [Tag]
|
|
90
|
+
init(title: String) { self.title = title; self.timestamp = .now; self.tags = [] }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
struct ItemListView: View {
|
|
94
|
+
@Query(sort: \Item.timestamp, order: .reverse) private var items: [Item]
|
|
95
|
+
@Environment(\.modelContext) private var context
|
|
96
|
+
|
|
97
|
+
var body: some View {
|
|
98
|
+
List(items) { Text($0.title) }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Vapor (Server-Side)
|
|
104
|
+
|
|
105
|
+
```swift
|
|
106
|
+
app.get("users", ":id") { req async throws -> User in
|
|
107
|
+
guard let id = req.parameters.get("id", as: UUID.self) else { throw Abort(.badRequest) }
|
|
108
|
+
guard let user = try await User.find(id, on: req.db) else { throw Abort(.notFound) }
|
|
109
|
+
return user
|
|
110
|
+
}
|
|
111
|
+
```
|
package/app/skills/test/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: test
|
|
3
|
-
description: "Run
|
|
3
|
+
description: "Run the project's test suite with coverage reporting, auto-detecting the framework (pytest, vitest, jest, flutter test, go test, cargo test, phpunit). Use when the user asks to run existing tests — not to author new ones test-first."
|
|
4
4
|
effort: medium
|
|
5
5
|
disable-model-invocation: true
|
|
6
6
|
argument-hint: "[file or pattern]"
|
|
@@ -105,3 +105,25 @@ tests/ # or test/, spec/, __tests__/
|
|
|
105
105
|
├── integration/ # Tests with external dependencies
|
|
106
106
|
└── e2e/ # End-to-end tests
|
|
107
107
|
```
|
|
108
|
+
|
|
109
|
+
## Rules
|
|
110
|
+
|
|
111
|
+
- **MUST** detect the framework automatically via `detect-runner.py` — do not assume
|
|
112
|
+
- **NEVER** modify tests to make them pass
|
|
113
|
+
- **CRITICAL**: coverage reporting must use the project's configured tool (`--cov=src`, `--coverage`, etc.) — do not invent flags
|
|
114
|
+
- **MANDATORY**: when a test fails, report the failure exactly; do not paraphrase
|
|
115
|
+
|
|
116
|
+
## Gotchas
|
|
117
|
+
|
|
118
|
+
- `pytest --cov=src` inflates coverage when `tests/` lives under `src/` — test files count toward covered lines. Either move `tests/` out, or use `--cov=src --cov-branch --cov-report=term-missing` with an explicit `[tool.coverage.run] omit = ["tests/*"]` in `pyproject.toml`.
|
|
119
|
+
- `go test ./...` runs packages in parallel by default; a test depending on shared global state may pass alone and fail in the suite. If flakiness appears only under `./...`, suspect shared state, not a bug in the test.
|
|
120
|
+
- `flutter test` without an emulator falls back to the headless "null platform" — widget tests that require a render surface are **silently skipped**. CI without a display must add `flutter test --platform vm` or a virtual framebuffer.
|
|
121
|
+
- `vitest run` and `jest` interpret glob patterns differently: `*.test.ts` in vitest matches filenames, in jest matches paths. Passing the same CLI arg to both produces different test sets — use the framework-native config file when possible.
|
|
122
|
+
- `pytest --lf` (last-failed) silently runs **all** tests if there is no cache from a prior run. First-time runs in CI therefore ignore `--lf` and re-run everything, which can hide "only failed tests" bugs in local runs.
|
|
123
|
+
|
|
124
|
+
## When NOT to Use
|
|
125
|
+
|
|
126
|
+
- To write new tests test-first — use `/tdd`
|
|
127
|
+
- To author test design patterns — use `/testing-patterns` (knowledge skill)
|
|
128
|
+
- To debug a failing test — use `/debug` after `/test` surfaces the failure
|
|
129
|
+
- For performance/load testing — use dedicated tooling, not `/test`
|
|
@@ -82,3 +82,29 @@ For Flutter/Dart testing patterns, see [reference/flutter-testing.md](reference/
|
|
|
82
82
|
| "We'll add tests later" | Untested code accumulates — later means never, and coverage gaps compound |
|
|
83
83
|
| "Mocking everything is fine" | Over-mocking tests the mocks, not the code — mock at boundaries only |
|
|
84
84
|
| "100% coverage means no bugs" | Coverage measures execution, not correctness — focus on behavior assertions |
|
|
85
|
+
|
|
86
|
+
## Rules
|
|
87
|
+
|
|
88
|
+
- **MUST** follow Arrange-Act-Assert (AAA) structure in every test — unstructured tests degrade into procedural smoke tests
|
|
89
|
+
- **MUST** test behavior through the public interface, not internal implementation — tests coupled to internals break on every refactor
|
|
90
|
+
- **NEVER** test implementation details (private method return values, internal state flags) — they are not the contract
|
|
91
|
+
- **NEVER** hit real external services in unit tests — use fakes/stubs for boundaries; save real integration for integration tests
|
|
92
|
+
- **CRITICAL**: integration tests must hit real dependencies (database, message queue, external API) when mock-vs-prod divergence is a real risk. Mocked integration tests create false confidence.
|
|
93
|
+
- **MANDATORY**: flaky tests are bugs, not noise. Quarantine or delete them — a tolerated flaky test erodes the suite's credibility.
|
|
94
|
+
|
|
95
|
+
## Gotchas
|
|
96
|
+
|
|
97
|
+
- Coverage numbers are easy to game: include generated code, test files that import but do not assert, or wide `# pragma: no cover` usage. A 95% reported coverage with 60% real behavior assertion is common.
|
|
98
|
+
- Snapshot tests (Jest `.toMatchSnapshot()`, pytest-regressions) accept any output as "correct" on first run. An incorrect initial snapshot becomes the accepted baseline — review snapshots as carefully as code.
|
|
99
|
+
- Mocks configured with `any` matchers (e.g., `.mock.calls[0][0]` without a schema) pass even when the production call shape changes. Assert on specific arguments, not just "was called".
|
|
100
|
+
- Test isolation fails when globals leak (module-level mutable state, module-scoped fixtures, env vars set in one test). Flakiness that appears only under `pytest -n auto` or `jest --parallel` is usually shared state.
|
|
101
|
+
- Property-based tests (Hypothesis, fast-check) shrink failing examples to minimal reproducers, but shrinking time can dominate the run. For complex generators, cap shrink deadlines or seed the failing example for next-run reproducibility.
|
|
102
|
+
- Test pyramid vs trophy: the "right" ratio depends on stack. Frontend apps with rendering concerns benefit from more integration tests (trophy); pure backend services align better with pyramid. Don't cargo-cult one model.
|
|
103
|
+
|
|
104
|
+
## When NOT to Load
|
|
105
|
+
|
|
106
|
+
- For **running** the test suite — use `/test`
|
|
107
|
+
- For test-first development workflow — use `/tdd`
|
|
108
|
+
- For debugging a specific test failure — use `/debug` on the failure output
|
|
109
|
+
- For test framework choice in a new project — use `/app-builder`
|
|
110
|
+
- For performance/load testing — this skill covers correctness tests, not load
|
|
@@ -114,7 +114,25 @@ Do NOT include file paths, line numbers, or implementation details. Describe mod
|
|
|
114
114
|
|
|
115
115
|
## Rules
|
|
116
116
|
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
-
|
|
120
|
-
-
|
|
117
|
+
- **MUST** explore the codebase for root cause before filing — symptoms masquerade as causes often
|
|
118
|
+
- **MUST** propose a TDD fix plan with ordered RED→GREEN cycles, each a vertical slice
|
|
119
|
+
- **NEVER** ask follow-up clarifying questions; one initial question maximum, then investigate autonomously
|
|
120
|
+
- **NEVER** include file paths, function names, or line numbers in the issue body — they go stale before the issue is picked up
|
|
121
|
+
- **CRITICAL**: the issue must be reproducible. If reproduction steps cannot be determined from the investigation, say so explicitly in the Problem section — do not fabricate them.
|
|
122
|
+
- **MANDATORY**: file the issue immediately via `gh issue create` and share the URL — do not ask the user to review a draft first
|
|
123
|
+
|
|
124
|
+
## Gotchas
|
|
125
|
+
|
|
126
|
+
- `gh issue create` without `--body` opens `$EDITOR`. In automated flows the skill hangs — always pass the body file or inline text.
|
|
127
|
+
- "Root cause" often turns out to be two concurrent issues. If the investigation keeps branching, file the most-probable primary cause and note the secondary as a follow-up in the same issue.
|
|
128
|
+
- TDD plans with more than ~5 RED→GREEN cycles usually conceal a deeper design issue. Short plans (2-3 cycles) reflect confident root-cause identification; long plans reflect fishing.
|
|
129
|
+
- The `debugger` agent explored autonomously but returns a narrative. Parse it for: confirmed hypothesis, code paths, and recent changes. Discard speculation.
|
|
130
|
+
- Bugs in framework-adjacent code (middleware, ORM hooks) require test setup that mirrors the framework's call context. A TDD plan that writes the test "like a unit test" may not actually reproduce the framework bug.
|
|
131
|
+
|
|
132
|
+
## When NOT to Use
|
|
133
|
+
|
|
134
|
+
- For a conversational bug report from a non-engineer — use `/qa-session` first, which returns refined reports this skill can then process
|
|
135
|
+
- For a specific reproducible error with known root cause — use `/fix` directly
|
|
136
|
+
- For architectural-scale problems — use `/architecture-audit`
|
|
137
|
+
- For creating issues from a PRD — use `/prd-to-issues`
|
|
138
|
+
- For debugging without filing an issue — use `/debug`
|
|
@@ -64,11 +64,26 @@ Extract and formalize domain terminology into a consistent glossary.
|
|
|
64
64
|
|
|
65
65
|
## Rules
|
|
66
66
|
|
|
67
|
-
- **
|
|
68
|
-
- **
|
|
69
|
-
- **
|
|
70
|
-
- **
|
|
71
|
-
- **
|
|
72
|
-
- **
|
|
73
|
-
- **
|
|
74
|
-
|
|
67
|
+
- **MUST** be opinionated — pick the best term and list alternatives as aliases to avoid
|
|
68
|
+
- **MUST** flag every ambiguity (one word → two concepts) and every synonym (two words → one concept) with an explicit recommendation
|
|
69
|
+
- **MUST** write tight definitions (one sentence) that define what the term **is**, not what it does
|
|
70
|
+
- **NEVER** include generic programming concepts (array, function, endpoint, class) — this glossary is for the **domain**, not the toolchain
|
|
71
|
+
- **NEVER** silently overwrite an existing `UBIQUITOUS_LANGUAGE.md` — read the current file first, then update incrementally preserving prior decisions
|
|
72
|
+
- **CRITICAL**: show relationships with cardinality ("an Order belongs to exactly one Customer"). Vocabulary without relationships is a word list, not a language.
|
|
73
|
+
- **MANDATORY**: include 3-5 example dialogue exchanges showing correct usage. Abstract glossaries without dialogue rarely get adopted.
|
|
74
|
+
|
|
75
|
+
## Gotchas
|
|
76
|
+
|
|
77
|
+
- Domain terms often **overlap** with framework jargon (e.g., "Service" in DDD vs "Service" in Angular). If the framework already claims a term, prefer a domain-specific alternative to avoid collision.
|
|
78
|
+
- Stakeholders resist terminology change even when their current terms are ambiguous. "Account" replacing "User" triggers more discussion than expected — plan for negotiation in the example dialogue section.
|
|
79
|
+
- Glossaries rot when features ship without updating them. A `UBIQUITOUS_LANGUAGE.md` last updated 6 months ago is a snapshot, not a source of truth. Flag staleness in the header and re-run this skill periodically.
|
|
80
|
+
- Relationships between terms are easy to hand-wave with "related to". Pin down the cardinality (`0..1`, `1..*`, `1..1`) — vague relationships produce schema ambiguity later.
|
|
81
|
+
- The opinionated canonical choice may contradict marketing or legal language. Note conflicts explicitly rather than hiding them; the domain model and the marketing site can legitimately diverge.
|
|
82
|
+
|
|
83
|
+
## When NOT to Use
|
|
84
|
+
|
|
85
|
+
- For writing a **PRD** (full requirements) — use `/write-a-prd`
|
|
86
|
+
- For generating user-facing documentation — use `/docs`
|
|
87
|
+
- For implementation planning — use `/plan` or `/prd-to-plan`
|
|
88
|
+
- For enforcing language in commit messages or code reviews — use `/brand-voice` (distinct concern: writing style, not domain terms)
|
|
89
|
+
- When the project is a single-developer throwaway — glossary overhead is not justified
|