From 802a341ad281d20436312193c27da8ecef06d9f1 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:02:59 -0700 Subject: [PATCH 01/12] docs(caveman): stop firing on a one-off "be brief" "Be brief" is an everyday instruction, but matching it engages a persistent persona that stays active until the user says "stop caveman". Keep only explicit mode invocations as triggers. --- .agents/skills/caveman/SKILL.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.agents/skills/caveman/SKILL.md b/.agents/skills/caveman/SKILL.md index 94dc32d4a9..74ca6301bd 100644 --- a/.agents/skills/caveman/SKILL.md +++ b/.agents/skills/caveman/SKILL.md @@ -4,7 +4,8 @@ description: > Ultra-compressed communication mode. Cuts token usage ~75% by dropping filler, articles, and pleasantries while keeping full technical accuracy. Use when user says "caveman mode", "talk like caveman", "use caveman", - "less tokens", "be brief", or invokes /caveman. + "less tokens", or invokes /caveman. A one-off "be brief" is not a trigger; + this mode persists until switched off. metadata: upstream: mattpocock/skills forked: 2026-05-17 From 0f0ba0ca5791deb0fe083632d80c6927f48db1d6 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:02:59 -0700 Subject: [PATCH 02/12] docs(control-flow): narrow triggers away from "simplify this" "Simplify this" collides with collapse-pass, fresh-eyes-grill, and the harness /simplify command, and most such asks are not about nesting. Also repair four space-colon artifacts left by an earlier em dash removal pass. --- .agents/skills/control-flow/SKILL.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.agents/skills/control-flow/SKILL.md b/.agents/skills/control-flow/SKILL.md index d121e35502..d8a6014483 100644 --- a/.agents/skills/control-flow/SKILL.md +++ b/.agents/skills/control-flow/SKILL.md @@ -1,6 +1,6 @@ --- name: control-flow -description: 'Control flow: early returns, guard clauses, linearizing nested logic. Use for "simplify this", "flatten these conditions", "too many nested ifs".' +description: 'Control flow: early returns, guard clauses, linearizing nested logic. Use for "flatten these conditions", "too many nested ifs", "linearize this try-catch", or handlers mixing throw and return. For a broad "simplify this" pass over a diff or package, use collapse-pass instead.' metadata: author: epicenter version: '1.0' @@ -131,9 +131,9 @@ async ({ body, status }) => { The transformation follows the same human reasoning pattern: -1. **Try the risky thing** : wrap only what can fail -2. **Check if it failed** : early return with the appropriate error -3. **Continue with the happy path** : the rest of the function assumes success +1. **Try the risky thing**: wrap only what can fail +2. **Check if it failed**: early return with the appropriate error +3. **Continue with the happy path**: the rest of the function assumes success This eliminates the nesting, makes `return` vs `throw` consistent, and separates the error boundary from the safe code that follows it. @@ -161,7 +161,7 @@ async ({ body, status }) => { }); if (error) return status('Bad Gateway', error.message); - // Happy path : all guards passed + // Happy path: all guards passed return toServerSentEventsResponse(stream); }; ``` From 12037074fc8fbaf60ee80e4a5769ba5eb3f315fd Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:02:59 -0700 Subject: [PATCH 03/12] docs(one-sentence-test): fix stale asymmetric-wins delegation The txt block still sent agents to run the asymmetric wins pass in cohesive-clean-breaks, but that pass moved to the asymmetric-wins skill; the paragraph above it already delegates there, so drop the re-taught steps. Also stop claiming the everyday phrase "what does X do", which audit-framed plain code-comprehension questions. --- .agents/skills/one-sentence-test/SKILL.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.agents/skills/one-sentence-test/SKILL.md b/.agents/skills/one-sentence-test/SKILL.md index 2027432784..de723ee490 100644 --- a/.agents/skills/one-sentence-test/SKILL.md +++ b/.agents/skills/one-sentence-test/SKILL.md @@ -1,6 +1,6 @@ --- name: one-sentence-test -description: "Force one concrete sentence to find orphaned surfaces, duplicate verbs, inert abstractions. Use for \"what does X do\", \"in one sentence\", \"too many options\"." +description: "Force one concrete sentence to find orphaned surfaces, duplicate verbs, inert abstractions. Use for \"what does X actually do\", \"in one sentence\", \"too many options\", or auditing whether a surface or abstraction is coherent. For a plain code-comprehension question, answer directly instead." metadata: author: epicenter version: '2.0' @@ -48,14 +48,6 @@ After the surface audit, run an asymmetric wins check. This skill only detects the opportunity; [asymmetric-wins](../asymmetric-wins/SKILL.md) owns the decision, candidate list, and refusal template. -```txt -1. List the convenience features, rare modes, old shapes, and fast paths. -2. Circle the one that forces the most extra surface area. -3. Remove that one from the sentence. -4. If the sentence still describes a useful product, run the asymmetric wins - pass in cohesive-clean-breaks. -``` - This matters most before greenfield implementation, when AI can make a second path feel cheap. The second path is still a permanent invariant. @@ -100,7 +92,7 @@ Triggers: - Before recommending wrapping, extending, or composing an existing utility: do the reduction first - When reviewing your own just-written abstraction before sending it - When code and docs seem to disagree, or docs describe capabilities you can't locate in the body -- User asks "what does X do" or "is this useful": don't paraphrase docs, do the reduction +- User asks "what does X actually do" or "is this useful": don't paraphrase docs, do the reduction. A plain comprehension question gets a direct answer, not this pass. Three reductions in order. Don't skip. From 1da128147a004160a46483295881d5e0d098a1c2 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:04:06 -0700 Subject: [PATCH 04/12] docs(progress-summary): scope triggers to work recaps Bare "can you summarize" and "what happened" fire on everyday asks (summarize this article, explain this stack trace) that have nothing to do with PR-style work recaps. Also repair three glued-colon artifacts from an earlier em dash removal pass and compress the git command block to the two non-obvious rules. --- .agents/skills/progress-summary/SKILL.md | 28 +++++------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/.agents/skills/progress-summary/SKILL.md b/.agents/skills/progress-summary/SKILL.md index d833a8ae2e..3c06a6c75c 100644 --- a/.agents/skills/progress-summary/SKILL.md +++ b/.agents/skills/progress-summary/SKILL.md @@ -1,6 +1,6 @@ --- name: progress-summary -description: 'Conversational PR-style summaries with visual diagrams. Use when: "can you summarize", "what happened", "where are we at", "give me an overview", "walk me through".' +description: 'Conversational PR-style summaries of work in progress, with visual diagrams. Use when: "summarize what we did", "where are we at", "walk me through the changes", or "give me an overview" of session or branch work. Not for summarizing an article, file, or codebase you did not change.' metadata: author: epicenter version: '1.0' @@ -16,7 +16,7 @@ For newcomer-friendly architecture explanations, use [notebook-explanation](../n ### Motivation First -Every summary starts with WHY. Not what files changed, not how it works:WHY this work matters. +Every summary starts with WHY. Not what files changed, not how it works: WHY this work matters. **Good opening**: > We've been tackling the session timeout issue that was logging users out mid-upload. The root cause was the session refresh only triggering on navigation, not during background activity. @@ -87,31 +87,13 @@ For "explain what's happening here" on larger work, use [notebook-explanation](. ## What to Avoid -- **Listing files changed**: "Updated auth.ts, session.ts, and upload.ts" : just explain what and why +- **Listing files changed**: "Updated auth.ts, session.ts, and upload.ts"; just explain what and why - **Corporate speak**: "This enhancement leverages our existing infrastructure" - **Marketing language**: "game-changing", "revolutionary", "seamless" -- **Dramatic hyperbole**: "excruciating pain point" : stick to facts +- **Dramatic hyperbole**: "excruciating pain point"; stick to facts - **Bullet point everything**: Use flowing paragraphs when possible - **Over-explaining simple changes**: Match the explanation depth to the complexity ## Gathering Context for Summaries -To generate a summary, gather relevant context: - -```bash -# Current branch state -git status -git log --oneline -10 - -# What changed from main -git diff main...HEAD --stat -git log main..HEAD --oneline - -# Recent activity -git log --oneline --since="1 hour ago" -``` - -If the environment provides a dedicated workspace-diff tool, use it. Otherwise -use `git diff --stat`, `git diff`, and targeted file reads. - -Read key files that were modified to understand the substance of changes, not just the diff stats. +Gather context from the branch log and `git diff main...HEAD --stat`; if the environment provides a dedicated workspace-diff tool, prefer it. Read key modified files to understand the substance of changes, not just the diff stats. From a147dfce8a990addb679280b8433807fc56c31f1 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:04:06 -0700 Subject: [PATCH 05/12] docs(change-proposal): stop claiming "what should we do" That phrase matches nearly every planning question, loading a heavyweight visual-proposal format where a plain answer is wanted. Collapse the four-way diagram-type menu to a default (ownership) with a single escape clause, and repair a glued-colon artifact. --- .agents/skills/change-proposal/SKILL.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.agents/skills/change-proposal/SKILL.md b/.agents/skills/change-proposal/SKILL.md index 3531cce220..2003b8b8c8 100644 --- a/.agents/skills/change-proposal/SKILL.md +++ b/.agents/skills/change-proposal/SKILL.md @@ -1,11 +1,11 @@ --- name: change-proposal -description: 'Present proposed code changes visually before implementing. Use when: "show me options", "compare approaches", "what should we do", or when changes need before/after comparison.' +description: 'Present proposed code changes visually before implementing: before/after diffs, ASCII diagrams, comparison tables. Use when: "show me options for this change", "compare approaches", or a multi-file change needs a before/after comparison before editing. Not for planning questions with no concrete code change on the table.' --- # Change Proposal -When proposing non-trivial changes, make your reasoning visible before acting. The user should see what will change, why, and what alternatives were considered:before a single file is edited. +When proposing non-trivial changes, make your reasoning visible before acting. The user should see what will change, why, and what alternatives were considered, before a single file is edited. Follow [writing-voice](../writing-voice/SKILL.md) for prose sections. @@ -15,7 +15,7 @@ Follow [writing-voice](../writing-voice/SKILL.md) for prose sections. - Changes span 3+ files (show the dependency graph) - Architecture or ownership shifts (show before/after diagrams) - Lifecycle or data flow changes (show the flow) -- The user asks "what do you think?" or "how should we do this?" +- The user asks "what do you think?" or "how should we do this?" about a concrete code change For trivial changes (typo fix, single-line edit, obvious bug), skip this and just do it. @@ -72,11 +72,7 @@ auth ──signOut()──→ workspace.current.dispose() workspace is a reactive slot ← auth owns lifecycle ``` -When to use which diagram type: -- **Ownership diagrams**: Who controls what (arrows show control flow) -- **Layer diagrams**: Stacked boxes for architectural layers -- **Flow diagrams**: Data or control moving between components -- **Journey diagrams**: Evolution from attempt A → B → C +Default to an ownership diagram: who controls what, with arrows showing control flow. Reach for stacked layers, a data-flow shape, or an attempt A → B → C journey only when ownership is not the question being decided. ### 3. Comparison Tables From 7f230dad3bc5ae76df01d52e47d8a3df0ccfe80d Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:05:12 -0700 Subject: [PATCH 06/12] docs(github-issues): stop canonizing one past reply The skill hardcoded artifacts of a single 2025 Whispering thread as unconditional rules: always include the cal.com link, claim "I'm free as early as tomorrow", reference v7.1.0 by number, and ask that thread's 4-second-recording follow-ups. Agents were set up to paste stale versions and fabricated availability into public replies. Make the call offer and Discord invite conditional, swap version literals for placeholders, generalize the follow-up questions, resolve the always-greet vs no-formulaic-openings contradiction, and cut the Writing Style Notes bullets that restate Anti-Patterns and writing-voice. --- .agents/skills/github-issues/SKILL.md | 32 ++++++++++----------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/.agents/skills/github-issues/SKILL.md b/.agents/skills/github-issues/SKILL.md index dd530a3480..ab78c1cd95 100644 --- a/.agents/skills/github-issues/SKILL.md +++ b/.agents/skills/github-issues/SKILL.md @@ -70,7 +70,7 @@ Thanks again for catching it! ## Opening Pattern -Always start with a personal greeting using the user's GitHub handle: +Open your first reply on a thread with a personal greeting using the user's GitHub handle (ongoing back-and-forth can skip it): - "Hey @username, thank you for the issue" - "Hey everyone, thanks for the notice!" @@ -98,23 +98,22 @@ For complex issues, offer direct help: - "If you have time, I would love to hop on a call with you, and we can debug this together" - "Let's hop on a call sometime in the coming days, and I'll debug it with you" -- Always include cal.com link: "https://cal.com/epicenter/whispering" -- Add availability: "I'm free as early as tomorrow" +- When offering a call, include the cal.com link: "https://cal.com/epicenter/whispering" +- Do not claim specific availability ("I'm free as early as tomorrow") you cannot verify ### 4. Discord Promotion -When appropriate, mention Discord: +Occasionally, when the reporter seems invested in the project, invite them to Discord: - "PS: I've also recently created a Discord group, and I'd love for you to join! You can ping me directly for more features." - Include link: "https://go.epicenter.so/discord" ### 5. Follow-up Questions -Ask clarifying questions to understand the issue better: +Ask clarifying questions to understand the issue better. Ask for whatever is missing: exact version, platform, and the smallest reproduction. -- "To clarify, could you confirm that this issue persists even with the latest v7.1.0 installer?" -- "Did you ever get a popup to grant permission to access recording devices?" -- "Does this happen when you make recordings for more than 4 seconds?" +- "To clarify, could you confirm that this issue persists even with the latest installer?" +- "Could you share which OS and version you're on?" ### 6. Closing @@ -130,7 +129,7 @@ End with gratitude: ### Feature Implementation Response ``` -Hey @username, thank you for the issue, and good news! [Whispering v7.1.0](link) now includes the [feature]! Thank you for the inspiration. +Hey @username, thank you for the issue, and good news! [The latest release](link) now includes the [feature]! Thank you for the inspiration. [Brief description of how it works] @@ -142,11 +141,11 @@ https://go.epicenter.so/discord ### Debugging Response ``` -Hey @username, so sorry to hear this! I apologize for the delayed response; I was finalizing [the latest release v7.1.0](link). +Hey @username, so sorry to hear this! I apologize for the delayed response; I was finalizing [the latest release](link). -To clarify, could you confirm that this issue persists even with the latest v7.1.0 installer? +To clarify, could you confirm that this issue persists even with the latest installer? -If you have time, I would love to hop on a call with you, and we can debug this together. You can book a meeting with me using my cal.com link right here, I'm free as early as tomorrow: +If you have time, I would love to hop on a call with you, and we can debug this together. You can book a meeting with me using my cal.com link right here: https://cal.com/epicenter/whispering @@ -173,13 +172,6 @@ Let me know if you want a confirmation prompt before clearing. ## Writing Style Notes -See [writing-voice](../writing-voice/SKILL.md) for punctuation and tone guidelines. - -- Use casual, approachable language -- Be genuinely enthusiastic about user contributions -- Reference specific users and give credit -- Link to relevant issues, releases, or commits -- Keep responses personal and conversational -- Avoid corporate or overly formal language +- Reference specific users and give credit; link to relevant issues, releases, or commits - PR comments can be brief: ongoing discussions don't need full greetings/closings - Match the energy: short question gets short answer, detailed report gets detailed response From 7fd21312b90e903f406b7ec0bd2f84f6f01ad7ec Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:05:56 -0700 Subject: [PATCH 07/12] docs(spec-execution): fix hygiene gate command and cut restated closers The mandated hygiene check ran bun scripts/check-doc-hygiene.mjs, but the script is check-doc-hygiene.ts, so the gate failed on every spec execution. Also delete the Quick Reference (a third rendering of the workflow already shown as the loop diagram and the phases), fold the commit-shape table back into the intro it restated, and drop the Length Panic anti-pattern that repeated Phase 0's large-specs-are-fine rule. Triple-rendered workflow copies drift out of sync. --- .agents/skills/spec-execution/SKILL.md | 44 ++------------------------ 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/.agents/skills/spec-execution/SKILL.md b/.agents/skills/spec-execution/SKILL.md index 9d569b08d8..69336502e6 100644 --- a/.agents/skills/spec-execution/SKILL.md +++ b/.agents/skills/spec-execution/SKILL.md @@ -174,14 +174,7 @@ If you discovered something during implementation, add it to the spec's Research Each committed unit should include BOTH the code changes AND the spec updates that describe those changes. This keeps history honest: each commit shows what was planned, what changed, and what was actually built. -You do not have to create one commit per wave. Use the commit shape that best fits the review: - -| Situation | Commit Shape | -| --- | --- | -| Wave is a self-contained foundation or behavior change | Commit the wave | -| Several waves are tightly coupled and make sense only together | Combine them into one larger commit | -| One wave contains multiple independently reviewable changes | Split it into logical commits | -| The user wants one large commit | Keep intermediate working checkpoints, then create one final commit | +You do not have to create one commit per wave; use the commit shape that best fits the review, as described at the top of this skill. If the user wants one large commit, keep intermediate working checkpoints and create one final commit at the end. Follow `git` and `standalone-commits` skill conventions: @@ -216,7 +209,7 @@ After all waves complete: landed, `git rm` the spec. Git keeps the body and `docs/spec-history.md` indexes it by date, so nothing is lost. Do not leave a finished spec in the tree as a knowledge base; that is the pollution this workflow exists to prevent. -4. **Verify hygiene.** Run `bun scripts/check-doc-hygiene.mjs`. It must pass: no +4. **Verify hygiene.** Run `bun scripts/check-doc-hygiene.ts`. It must pass: no spec left in the tree declaring a terminal status, no `Proposed` ADR orphaned by a deleted spec. A failure means the harvest is incomplete; fix it (flip the ADR, delete the spec) rather than committing the smell. @@ -277,14 +270,6 @@ Code diverges from spec but spec is never updated The spec is a living document. Every commit should leave the spec accurate to what was actually built. -### Length Panic - -``` -This spec is long, so split it before reading it -``` - -No. Length is not the failure mode. Confusing reader jobs are the failure mode. A long spec with a clear first screen, current path, decisions, and verification can be easier to execute than five scattered short specs. - ### Over-Parallel ``` @@ -292,28 +277,3 @@ Wave 1: 12 sub-agents all running at once ``` More than 3-4 parallel sub-agents gets chaotic. Group related tasks and keep parallelism manageable. - -## Quick Reference - -Before starting: - -- [ ] Preflight status, supersession, and active path -- [ ] Read active sections first -- [ ] Identify phases and dependencies -- [ ] Plan waves (parallel vs sequential) -- [ ] Surface decisions that need user input - -For each wave: - -- [ ] Execute tasks (sub-agents when useful) -- [ ] Verify (type-check, tests) -- [ ] Update spec (check items, add notes) -- [ ] Commit if this wave is a logical review unit, or checkpoint and continue - -After all waves: - -- [ ] Run `post-implementation-review` on touched files -- [ ] Harvest durable decisions into `docs/adr/` (new ADR, or flip a `Proposed` one to `Accepted`) -- [ ] Delete the spent spec (`git rm`; git and `docs/spec-history.md` keep the history) -- [ ] Run `bun scripts/check-doc-hygiene.mjs` (must pass) -- [ ] Finalize commits (ADR + spec deletion) using the chosen strategy From 21c39413d6091083fd06afb2ecf76fe41be53d0b Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:07:13 -0700 Subject: [PATCH 08/12] docs(specification-writing): delete the three restating closers Good vs Bad Specs, Writing for Agent Implementers, and the Quick Reference Checklist each re-rendered the Document Structure and Core Philosophy sections item for item, so every spec-writing task loaded the same guidance three times and the copies could drift apart. Keep the two novel pieces (the process-theater rule moves into Core Philosophy, the too-prescriptive/too-vague sweet spot stays as the closer) and collapse the duplicated Adjacent Work philosophy block into the template section. --- .agents/skills/specification-writing/SKILL.md | 102 +----------------- 1 file changed, 5 insertions(+), 97 deletions(-) diff --git a/.agents/skills/specification-writing/SKILL.md b/.agents/skills/specification-writing/SKILL.md index c41f99a0de..c6ee726466 100644 --- a/.agents/skills/specification-writing/SKILL.md +++ b/.agents/skills/specification-writing/SKILL.md @@ -43,6 +43,7 @@ Specs should: - **Leave questions open**: The Open Questions section is a feature, not a bug - **Enable autonomous implementation**: An agent reading this should spawn sub-agents to verify and extend - **Respect maintainer time**: Make the active path obvious before asking the reader to absorb history, appendices, or implementation logs +- **No process theater**: Include a section because it changes implementation or review, not because the template lists it A good spec is a launching pad, not a script to follow. @@ -126,25 +127,10 @@ Never let evidence questions hide behind design coherence. Verify before deletin --- -## Adjacent Work - -Most specs do not need a section for adjacent work. When adjacent work matters, -write only what the implementer needs: - -```txt -Deferred: - not required now, allowed later - -Opportunistic: - not required, but acceptable to fix if discovered and grounded -``` - -A spec is an execution spine, not a file whitelist. - ---- - ## Document Structure +Not every spec needs every section. A small feature might skip Research Findings. A migration spec might focus heavily on Edge Cases. Use judgment. + ### Header (Required) ```markdown @@ -418,6 +404,8 @@ This section signals "you decide this" to the implementer. Include your recommen ### Adjacent Work +Most specs do not need this section; a spec is an execution spine, not a file whitelist. When adjacent work matters, write only what the implementer needs: deferred items (not required now, allowed later) and opportunistic ones (acceptable to fix if discovered and grounded). + ```markdown ## Adjacent Work @@ -459,84 +447,4 @@ Files that will be touched or consulted. - `[path/to/pattern.ts]` - [Pattern to follow or reference] ``` ---- - -## Good vs Bad Specs - -### Good Spec Characteristics - -- **Research is documented**: Shows what was explored, not just conclusions -- **Decisions have rationale**: Every choice has a "why" in a table -- **Questions are left open**: Implementer has room to decide -- **Code shows current state**: Not described abstractly -- **Architecture is visual**: ASCII diagrams over prose -- **Phases are actionable**: Checkboxes that can be tracked -- **First screen is useful**: Status, one sentence, current shape, target shape, and proof are easy to find -- **History is labeled**: Superseded decisions and implementation notes do not masquerade as active instructions - -### Bad Spec Characteristics - -- **Prescriptive step-by-step**: Reads like a tutorial, no room for autonomy -- **Assumes without research**: "We'll use X" without exploring alternatives -- **Closes all questions**: No Open Questions section -- **Abstract descriptions**: "The system will handle Y" without showing code -- **Wall of prose**: No tables, no diagrams, no structure -- **Reader-job mixing**: North-star architecture, execution prompt, handoff notes, and historical debate are all interleaved without a read path -- **Process theater**: Sections exist because the template said so, not because they change implementation or review - ---- - -## Writing for Agent Implementers - -When an agent reads your spec, they should: - -1. **Understand the problem** from Motivation section -2. **Know what's been explored** from Research Findings -3. **See the proposed direction** from Design Decisions -4. **Have a starting point** from Implementation Plan Phase 1 -5. **Know what to investigate further** from Open Questions - -The agent will then: - -- Spawn sub-agents to verify your research -- Explore the Open Questions you left -- Flesh out later phases of the implementation plan -- Make decisions where you left room - If your spec is too prescriptive, the agent will blindly follow it. If it's too vague, the agent will flounder. The sweet spot is: **enough context to start, enough openness to own the implementation**. - ---- - -## Quick Reference Checklist - -```markdown -- [ ] Header (Date, Status, Owner) -- [ ] One Sentence -- [ ] First screen answers active status, current shape, target shape, and proof -- [ ] "How to read this spec" block when the file is long or partly historical -- [ ] Overview (1-2 sentences) -- [ ] Motivation - - [ ] Current State (with code) - - [ ] Problems (numbered) - - [ ] Desired State -- [ ] Research Findings - - [ ] Comparison tables - - [ ] Key findings - - [ ] Implications -- [ ] Design Decisions (table with rationale) -- [ ] Design decisions have classes -- [ ] Class 1 decisions were verified -- [ ] Class 3 keeps are logged with `Revisit when:` -- [ ] Adjacent work is included only when it clarifies implementation -- [ ] Catalogs (when introducing a primitive set: code block + rejected candidates table) -- [ ] Architecture (ASCII diagrams) -- [ ] Visual rhythm: prose is broken up with code, tables, trees, or diagrams where relationships matter -- [ ] Call Sites (before/after on 2-3 real usages with file:line) -- [ ] Implementation Plan (phased checkboxes) -- [ ] Edge Cases -- [ ] Open Questions (with recommendations) -- [ ] Success Criteria -- [ ] References -``` - -Not every spec needs every section. A small feature might skip Research Findings. A migration spec might focus heavily on Edge Cases. Use judgment. From 4966002a26fe220957897adc985a541ba45ffeb1 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:09:27 -0700 Subject: [PATCH 09/12] docs(error-handling): stop teaching console.* and cut restated examples The Pattern's own snippets and the Examples block used console.log in service-shaped code, contradicting the repo's no-console-in-library rule and this skill's own Logging section; agents copy examples verbatim, so the skill was actively teaching the violation. Delete the Examples block (a case-for-case restatement of The Pattern), compress the trySync/tryAsync halves of the when-to-use taxonomy down to the try-catch escape hatch that actually needs stating, align the corrupted-row example with the canonical log.warn pattern, and repair the space-colon artifacts left by an earlier em dash removal pass. --- .agents/skills/error-handling/SKILL.md | 106 +++++-------------------- 1 file changed, 22 insertions(+), 84 deletions(-) diff --git a/.agents/skills/error-handling/SKILL.md b/.agents/skills/error-handling/SKILL.md index de15389764..57c5099905 100644 --- a/.agents/skills/error-handling/SKILL.md +++ b/.agents/skills/error-handling/SKILL.md @@ -26,7 +26,7 @@ Load these on demand based on what you're working on: - If working with **toast notifications for errors** (`toastOnError`, `extractErrorMessage` in UI), read [references/toast-on-error.md](references/toast-on-error.md) - If working with **real-world codebase examples and wrapping scenario guidelines**, read [references/real-world-examples.md](references/real-world-examples.md) - If working with **HTTP route handlers and status-response error conversion**, read [references/http-handlers.md](references/http-handlers.md) -- If working with **workspace actions** (`defineQuery` / `defineMutation` : when to throw vs. return `Err`, how remote callers see your errors, `ActionFailed` semantics), read [../workspace-api/references/action-return-shapes.md](../workspace-api/references/action-return-shapes.md) +- If working with **workspace actions** (`defineQuery` / `defineMutation`: when to throw vs. return `Err`, how remote callers see your errors, `ActionFailed` semantics), read [../workspace-api/references/action-return-shapes.md](../workspace-api/references/action-return-shapes.md) ## Use trySync/tryAsync Instead of try-catch for Graceful Error Handling @@ -47,7 +47,6 @@ const { data, error } = trySync({ }, catch: (e) => { // Gracefully handle parsing/validation errors - console.log('Using default configuration'); return Ok(defaultConfig); // Return Ok with fallback }, }); @@ -57,11 +56,9 @@ await tryAsync({ try: async () => { const child = new Child(session.pid); await child.kill(); - console.log(`Process killed successfully`); }, catch: (e) => { - // Gracefully handle the error - console.log(`Process was already terminated`); + // Process was already terminated; nothing to do return Ok(undefined); // Return Ok(undefined) for void functions }, }); @@ -72,7 +69,7 @@ const syncResult = trySync({ catch: (error) => { // For recoverable errors, return Ok with fallback value return Ok('fallback-value'); - // For unrecoverable errors, pass the raw cause : the constructor handles extractErrorMessage + // For unrecoverable errors, pass the raw cause: the constructor handles extractErrorMessage return CompletionError.ConnectionFailed({ cause: error }); }, }); @@ -135,72 +132,24 @@ if (error) { } ``` -### Examples +## Consuming Result values: destructure `error` explicitly -```typescript -// SYNCHRONOUS: JSON parsing with fallback -const { data: config } = trySync({ - try: () => JSON.parse(configString), - catch: (e) => { - console.log('Invalid config, using defaults'); - return Ok({ theme: 'dark', autoSave: true }); - }, -}); - -// SYNCHRONOUS: File system check -const { data: exists } = trySync({ - try: () => fs.existsSync(path), - catch: () => Ok(false), // Assume doesn't exist if check fails -}); - -// ASYNCHRONOUS: Graceful process termination -await tryAsync({ - try: async () => { - await process.kill(); - }, - catch: (e) => { - console.log('Process already dead, continuing...'); - return Ok(undefined); - }, -}); - -// ASYNCHRONOUS: File operations with fallback -const { data: content } = await tryAsync({ - try: () => readFile(path), - catch: (e) => { - console.log('File not found, using default'); - return Ok('default content'); - }, -}); - -// EITHER: Error propagation (works with both) -// Pass the raw caught error as cause : the defineErrors constructor calls extractErrorMessage -const { data, error } = await tryAsync({ - try: () => criticalOperation(), - catch: (error) => - CompletionError.ConnectionFailed({ cause: error }), -}); -if (error) return Err(error); -``` - -## Consuming Result values : destructure `error` explicitly - -When reading a `Result` that a library (or your own code) returns -: like `table.get(id)`, `tryAsync(...)`, or a service method : **always +When reading a `Result` that a library (or your own code) returns, +like `table.get(id)`, `tryAsync(...)`, or a service method, **always destructure both `data` and `error` and check `error` on its own line**, even when both paths should produce the same action. ```typescript -// ✅ GOOD : error is destructured and checked explicitly +// ✅ GOOD: error is destructured and checked explicitly const { data: row, error } = table.get(id); if (error) { - console.warn('[context] corrupted row:', error.message); + log.warn(error); return null; } if (row === null) return null; // legitimate absence use(row); // row: TRow -// ❌ BAD : relies on "data is null if error exists" by coincidence +// ❌ BAD: relies on "data is null if error exists" by coincidence const { data: row } = table.get(id); // error silently swallowed if (row === null) return null; use(row); @@ -220,10 +169,10 @@ Why: If both cases *genuinely* produce the same action (no log, no toast, no retry, no distinction worth writing down), one combined condition -is fine : as long as `error` is still destructured: +is fine, as long as `error` is still destructured: ```typescript -// ✅ OK : error destructured, both cases deliberately collapsed +// ✅ OK: error destructured, both cases deliberately collapsed const { data: row, error } = table.get(id); if (error || row === null) continue; // skip in both cases use(row); @@ -251,36 +200,25 @@ if (row === null) { use(row); ``` -This is the form to prefer by default : collapse back only when +This is the form to prefer by default; collapse back only when there's truly nothing distinct to say. -## When to Use trySync vs tryAsync vs try-catch - -- **Use trySync when**: - - Working with synchronous operations (JSON parsing, validation, calculations) - - You need immediate Result types without promises - - Handling errors in synchronous utility functions - - Working with filesystem sync operations +## When traditional try-catch is still right -- **Use tryAsync when**: - - Working with async/await operations - - Making network requests or database calls - - Reading/writing files asynchronously - - Any operation that returns a Promise +`trySync` covers synchronous work and `tryAsync` covers anything returning a Promise. Keep a traditional try-catch only when: -- **Use traditional try-catch when**: - - In module-level initialization code where you can't await - - For simple fire-and-forget operations - - When you're outside of a function context - - When integrating with code that expects thrown exceptions +- In module-level initialization code where you can't await +- For simple fire-and-forget operations +- When you're outside of a function context +- When integrating with code that expects thrown exceptions ## Logging errors -Typed errors are structured values, so they're also what the `wellcrafted/logger` wants. `log.warn` / `log.error` take a typed error unary : no message argument, no format string. The error owns its message, and the log sink gets the full object (name, fields, cause) alongside it. +Typed errors are structured values, so they're also what the `wellcrafted/logger` wants. `log.warn` / `log.error` take a typed error unary: no message argument, no format string. The error owns its message, and the log sink gets the full object (name, fields, cause) alongside it. ### The canonical pattern -Mint the typed error inside `catch:`, then branch on the Result and log inside the branch. The caller picks the level (`.warn` for recoverable, `.error` for loud) at the call site : matching Rust's `tracing::warn!(?err)` convention, where level lives at the call site and never on the error variant. +Mint the typed error inside `catch:`, then branch on the Result and log inside the branch. The caller picks the level (`.warn` for recoverable, `.error` for loud) at the call site, matching Rust's `tracing::warn!(?err)` convention, where level lives at the call site and never on the error variant. ```ts import { createLogger } from 'wellcrafted/logger'; @@ -309,11 +247,11 @@ Most epicenter call sites need the Ok branch's data locally, so they branch firs `log.warn` / `log.error` accept either the raw tagged error (`result.error` after narrowing) or the `Err`-wrapped factory output (`MyError.Variant({ ... })`) and unwrap structurally. -For the rarer Result-chain shape (`tryAsync(...).then(...)` where the Result flows out of the function), `tapErr(log.warn)` from `wellcrafted/result` is the combinator : see the logging SKILL's See also section. +For the rarer Result-chain shape (`tryAsync(...).then(...)` where the Result flows out of the function), `tapErr(log.warn)` from `wellcrafted/result` is the combinator; see the logging SKILL's See also section. ### Why no `log.error(message, error)`? -Level is context-dependent (same error can be `warn` on a retry, `error` on the last attempt) and message lives on the error variant. That's the whole point of `defineErrors` : the variant's `message:` template encodes the "what operation failed" clause. Duplicating it at the call site would drift and rot. +Level is context-dependent (same error can be `warn` on a retry, `error` on the last attempt) and message lives on the error variant. That's the whole point of `defineErrors`: the variant's `message:` template encodes the "what operation failed" clause. Duplicating it at the call site would drift and rot. ### Testing with `memorySink` From c2ce3ea7a68fe6cd6b307162fbf6f1e7f25bed61 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:11:07 -0700 Subject: [PATCH 10/12] docs(svg-animations): cut the copied tutorial down to the earned gotchas The skill was a 421-line generic essay (coordinate systems, shape primitives, a path-command table, gradient syntax) with no repo-specific line anywhere; a competent agent writes that SVG identically without the skill, and every icon-adjacent task paid the full load. Keep what an agent actually gets wrong: SMIL-vs-CSS reach in img contexts, transform-origin defaulting to the viewBox origin, fill=freeze snap-back, morph command matching, getTotalLength, GPU compositing, and reduced motion. Move the seven ready-made recipes to references/recipes.md loaded on demand, and narrow the description so static SVG and icon-layout asks stop triggering it. Also removes 21 space-colon artifacts from an earlier em dash strip. --- .agents/skills/svg-animations/SKILL.md | 399 +----------------- .../svg-animations/references/recipes.md | 110 +++++ 2 files changed, 133 insertions(+), 376 deletions(-) create mode 100644 .agents/skills/svg-animations/references/recipes.md diff --git a/.agents/skills/svg-animations/SKILL.md b/.agents/skills/svg-animations/SKILL.md index b2bcbff6a2..a2d6c25940 100644 --- a/.agents/skills/svg-animations/SKILL.md +++ b/.agents/skills/svg-animations/SKILL.md @@ -1,148 +1,32 @@ --- name: svg-animations -description: 'SVG animations: SMIL, CSS, path drawing, shape morphing, motion paths, gradients, masks, filters. Use for SVG graphics, icons, animated logos, spinners.' +description: 'SVG animation: SMIL, CSS keyframes, stroke path drawing, shape morphing, motion paths. Use when animating an SVG (spinners, animated logos, draw-in effects, morphing icons). Not for static SVG drawing or icon layout.' --- -This skill guides creation of handcrafted SVG animations : from simple animated icons to complex multi-stage path animations. SVGs are a markup language for images; every element is a DOM node you can style, animate, and script. +Handcrafted SVG animation. Every SVG element is a DOM node you can style, animate, and script; the craft is picking the right animation layer and dodging the handful of gotchas below. -## SVG Fundamentals +Read [references/recipes.md](references/recipes.md) when you need a ready-made pattern: loading spinner, animated checkmark, hamburger-to-X morph, motion along a path, gradient color shift, pulsing glow, or wave. -### Coordinate System -SVGs use a coordinate system defined by `viewBox="minX minY width height"`. The viewBox is your canvas : all coordinates are relative to it, making SVGs resolution-independent. +## Choose the Animation Layer -```svg - - - -``` - -### Shape Primitives - -```svg - - - - - - -``` - -### The `` Element : The Power Tool - -The `d` attribute defines a path using commands. Uppercase = absolute, lowercase = relative. - -| Command | Purpose | Syntax | -|---------|---------|--------| -| M/m | Move to | `M x y` | -| L/l | Line to | `L x y` | -| H/h | Horizontal line | `H x` | -| V/v | Vertical line | `V y` | -| C/c | Cubic bézier | `C x1 y1, x2 y2, x y` | -| S/s | Smooth cubic bézier | `S x2 y2, x y` | -| Q/q | Quadratic bézier | `Q x1 y1, x y` | -| T/t | Smooth quadratic | `T x y` | -| A/a | Elliptical arc | `A rx ry rotation large-arc sweep x y` | -| Z/z | Close path | `Z` | - -**Cubic Bézier** (`C`): Two control points define the curve. The first control point sets the departure angle, the second sets the arrival angle. - -```svg - -``` - -**Smooth Cubic** (`S`): Reflects the previous control point automatically : perfect for chaining fluid S-curves. - -```svg - -``` - -**Arc** (`A`): `rx ry x-rotation large-arc-flag sweep-flag x y` -- `large-arc-flag`: 0 = small arc, 1 = large arc (>180°) -- `sweep-flag`: 0 = counterclockwise, 1 = clockwise - -```svg - - -``` - -### Grouping and Transforms - -```svg - - - -``` - -Use `` to group elements for collective transforms, styling, and animation targets. - -### Gradients, Masks, and Filters - -```svg - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - ---- - -## CSS Animations on SVG - -Many SVG attributes are valid CSS properties: `fill`, `stroke`, `opacity`, `transform`, `stroke-dasharray`, `stroke-dashoffset`, etc. +SMIL (``, ``, ``, ``) lives inside the SVG markup and keeps working when the file is loaded via `` or CSS `background-image`, where CSS and JS cannot reach. Prefer SMIL for self-contained assets (icons, logos); use CSS keyframes when the SVG is inlined and the animation should coordinate with the rest of the page. -### Basic CSS Animation +## Gotchas That Bite -```css -.pulse { - animation: pulse 2s ease-in-out infinite; - transform-origin: center; -} -@keyframes pulse { - 0%, 100% { transform: scale(1); opacity: 1; } - 50% { transform: scale(1.15); opacity: 0.7; } -} -``` +- `transform-origin: center`: SVG transforms default to the viewBox origin (0,0), not the element center. Set it explicitly in CSS, or pass explicit center coordinates in SMIL (`from="0 25 25" to="360 25 25"`). +- `fill="freeze"` on SMIL animations keeps the final state; without it the element snaps back to the start. +- Shape morphing (SMIL `values` on `d`, or the CSS `d` property) interpolates only between paths with the same number and types of commands in the same order. If shapes differ, add invisible intermediate points to equalize. +- Use `path.getTotalLength()` for exact lengths in stroke-drawing animations instead of guessing dash values. +- Size with `viewBox` only; hardcoded `width`/`height` breaks resolution independence. Put gradients, filters, masks, and reusable shapes in ``. +- `stroke-linecap="round"` makes line animations look polished. -### Stroke Drawing Animation (The Classic) +## The Stroke Drawing Trick -The most iconic SVG animation. Uses `stroke-dasharray` and `stroke-dashoffset` to make a path appear to draw itself. - -**How it works:** -1. Set `stroke-dasharray` to the path's total length (one giant dash + one giant gap) -2. Set `stroke-dashoffset` to the same length (shifts the dash off-screen) -3. Animate `stroke-dashoffset` to 0 (slides the dash into view) +Set `stroke-dasharray` and `stroke-dashoffset` to the path length, then animate the offset to 0 so the path draws itself: ```svg - - - - + ``` -**Getting exact path length in JS:** -```js -const path = document.querySelector('.draw'); -const length = path.getTotalLength(); -path.style.strokeDasharray = length; -path.style.strokeDashoffset = length; -``` - -### Staggered Multi-Path Drawing +Stagger multiple paths with `animation-delay`. -```css -.line-1 { animation-delay: 0s; } -.line-2 { animation-delay: 0.3s; } -.line-3 { animation-delay: 0.6s; } -``` +## SMIL Timing and Easing -### CSS `d` Property Animation +Chain animations by id instead of hand-summing delays: -Modern browsers support animating the `d` attribute directly in CSS: - -```css -path { - d: path("M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z"); - transition: d 0.5s ease; -} -path:hover { - d: path("M 10,50 A 20,20 0,0,1 50,10 A 20,20 0,0,1 90,50 Q 90,80 50,100 Q 10,80 10,50 z"); -} -``` - -**Requirement:** Both paths must have the same number and types of commands for interpolation to work. - ---- - -## SMIL Animations (Native SVG) - -SMIL animations are declared directly inside SVG markup. They work even when SVG is loaded as an `` or CSS `background-image` : where CSS and JS can't reach. - -### `` : Animate Any Attribute - -```svg - - - -``` - -With keyframes: ```svg - -``` - -### `` : Transform Animations - -```svg - - - -``` - -Types: `translate`, `scale`, `rotate`, `skewX`, `skewY` - -### `` : Move Along a Path - -```svg - - - - - - -``` - -`rotate="auto"` orients the element tangent to the path. `rotate="auto-reverse"` flips it 180°. - -### `` : Discrete Value Changes - -```svg - - - -``` - -### Timing and Synchronization - -```svg - - ``` -Trigger values: -- `begin="click"` : on click -- `begin="2s"` : after 2 seconds -- `begin="other.end"` : when another animation ends -- `begin="other.end + 1s"` : 1s after another ends -- `begin="other.repeat(2)"` : on 2nd repeat of another - -### Easing with `calcMode` and `keySplines` - -```svg - -``` - -`calcMode` options: `linear` (default), `discrete`, `paced`, `spline` - -`keySplines` takes cubic-bezier control points (x1 y1 x2 y2) per interval. Common easings: -- Ease-in-out: `0.42 0 0.58 1` -- Ease-out: `0 0 0.58 1` -- Bounce-ish: `0.34 1.56 0.64 1` - -### Shape Morphing with SMIL - -Both shapes must have identical command structures (same number of points, same command types): - -```svg - - - -``` - ---- - -## Animation Patterns & Recipes - -### Loading Spinner - -```svg - - - - - - -``` - -### Animated Checkmark - -```svg - - - - -``` - -### Morphing Hamburger to X - -```svg - - - - - - - - - - - -``` - -### Gradient Animation (Color Shift) - -```svg - - - - - - - - - - - -``` - -### Breathing / Pulsing Glow - -```svg - - - - -``` - -### Wave / Liquid Effect - -```svg - - - -``` - ---- - -## Best Practices - -1. **Use `viewBox`, never hardcode `width`/`height` in the SVG** : let the container size it. This keeps it resolution-independent. - -2. **`` for reusable definitions** : gradients, filters, masks, clipPaths, and reusable shapes belong in ``. - -3. **Prefer SMIL for self-contained SVGs** (icons, logos loaded via ``) : CSS/JS won't work there. Use CSS animations when the SVG is inlined and you want to coordinate with the rest of the page. - -4. **Shape morphing requires matching commands** : same number of path commands, same types, same order. If shapes differ, add invisible intermediate points to equalize. - -5. **`stroke-linecap="round"`** makes line animations look polished. - -6. **`fill="freeze"`** in SMIL keeps the final animation state. Without it, the element snaps back. - -7. **`transform-origin: center`** in CSS : SVG transforms default to the origin (0,0), not the element center. Always set this explicitly. - -8. **Use `getTotalLength()`** in JS to get exact path lengths for stroke animations instead of guessing. - -9. **Layer animations with `` groups** : animate the group transform separately from individual element properties for complex choreography. +`begin` also accepts `click`, `2s`, `other.end + 1s`, and `other.repeat(2)`. For easing, use `calcMode="spline"` with `keySplines` cubic-bezier control points per interval (ease-in-out is `0.42 0 0.58 1`); the default is linear. -10. **Performance:** SVG animations are GPU-composited when animating `transform` and `opacity`. Animating `d`, `points`, or layout attributes triggers repaints : use sparingly on complex SVGs. +## Performance and Accessibility -11. **`will-change: transform`** on animated SVG elements helps the browser optimize compositing. +Animating `transform` and `opacity` is GPU-composited; animating `d`, `points`, or layout attributes triggers repaints, so use those sparingly on complex SVGs. `will-change: transform` helps the browser optimize compositing. -12. **Accessibility:** Add `role="img"` and `` / `<desc>` elements. Use `prefers-reduced-motion` media query to disable animations for users who request it: +Add `role="img"` and `<title>`/`<desc>`, and honor reduced motion: ```css @media (prefers-reduced-motion: reduce) { diff --git a/.agents/skills/svg-animations/references/recipes.md b/.agents/skills/svg-animations/references/recipes.md new file mode 100644 index 0000000000..e0a0280823 --- /dev/null +++ b/.agents/skills/svg-animations/references/recipes.md @@ -0,0 +1,110 @@ +# SVG Animation Recipes + +Copy-paste starting points. Each is self-contained; adjust colors, sizes, and durations in place. The gotchas in SKILL.md (transform centers, `fill="freeze"`, morph command matching) apply throughout. + +## Loading Spinner + +```svg +<svg viewBox="0 0 50 50"> + <circle cx="25" cy="25" r="20" fill="none" stroke="#1a1a1a" + stroke-width="3" stroke-linecap="round" + stroke-dasharray="90 150" stroke-dashoffset="0"> + <animateTransform attributeName="transform" type="rotate" + from="0 25 25" to="360 25 25" dur="1s" + repeatCount="indefinite" /> + <animate attributeName="stroke-dashoffset" values="0;-280" + dur="1.5s" repeatCount="indefinite" /> + </circle> +</svg> +``` + +## Animated Checkmark + +```svg +<svg viewBox="0 0 52 52"> + <circle cx="26" cy="26" r="24" fill="none" stroke="#4caf50" + stroke-width="2" class="draw" + style="stroke-dasharray:150;stroke-dashoffset:150; + animation:draw .6s ease forwards" /> + <path fill="none" stroke="#4caf50" stroke-width="3" + stroke-linecap="round" stroke-linejoin="round" + d="M14 27l7 7 16-16" class="draw" + style="stroke-dasharray:50;stroke-dashoffset:50; + animation:draw .4s ease .5s forwards" /> +</svg> +``` + +## Morphing Hamburger to X + +```svg +<svg viewBox="0 0 24 24" id="menu"> + <path id="top" d="M 3,6 L 21,6" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round"> + <animate attributeName="d" to="M 5,5 L 19,19" dur="0.3s" begin="menu.click" fill="freeze" /> + </path> + <path id="mid" d="M 3,12 L 21,12" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round"> + <animate attributeName="opacity" to="0" dur="0.1s" begin="menu.click" fill="freeze" /> + </path> + <path id="bot" d="M 3,18 L 21,18" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round"> + <animate attributeName="d" to="M 5,19 L 19,5" dur="0.3s" begin="menu.click" fill="freeze" /> + </path> +</svg> +``` + +## Move Along a Path + +`rotate="auto"` orients the element tangent to the path; `rotate="auto-reverse"` flips it 180 degrees. + +```svg +<circle r="5" fill="#e63946"> + <animateMotion dur="3s" repeatCount="indefinite" rotate="auto"> + <mpath href="#motionPath" /> + </animateMotion> +</circle> +<path id="motionPath" d="M 20,50 C 20,0 80,0 80,50 S 140,100 140,50" + fill="none" stroke="#ccc" /> +``` + +## Gradient Animation (Color Shift) + +```svg +<defs> + <linearGradient id="shift" x1="0%" y1="0%" x2="100%" y2="0%"> + <stop offset="0%"> + <animate attributeName="stop-color" + values="#e63946;#457b9d;#2a9d8f;#e63946" + dur="4s" repeatCount="indefinite" /> + </stop> + <stop offset="100%"> + <animate attributeName="stop-color" + values="#457b9d;#2a9d8f;#e63946;#457b9d" + dur="4s" repeatCount="indefinite" /> + </stop> + </linearGradient> +</defs> +<rect width="200" height="100" fill="url(#shift)" rx="8" /> +``` + +## Breathing / Pulsing Glow + +```svg +<circle cx="100" cy="100" r="30" fill="#e63946"> + <animate attributeName="r" values="30;35;30" dur="2s" + calcMode="spline" keySplines="0.4 0 0.6 1;0.4 0 0.6 1" + repeatCount="indefinite" /> + <animate attributeName="opacity" values="1;0.6;1" dur="2s" + calcMode="spline" keySplines="0.4 0 0.6 1;0.4 0 0.6 1" + repeatCount="indefinite" /> +</circle> +``` + +## Wave / Liquid Effect + +```svg +<path fill="#457b9d" opacity="0.7"> + <animate attributeName="d" dur="5s" repeatCount="indefinite" + values="M 0,40 C 30,35 70,45 100,40 L 100,100 L 0,100 Z; + M 0,40 C 30,50 70,30 100,40 L 100,100 L 0,100 Z; + M 0,40 C 30,35 70,45 100,40 L 100,100 L 0,100 Z" + calcMode="spline" keySplines="0.4 0 0.6 1;0.4 0 0.6 1" /> +</path> +``` From 754fddbb381e694801228572037e8ffa7a7896d1 Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:23:07 -0700 Subject: [PATCH 11/12] docs(skill-creator): encode the audit's recurring failures as detectors 5-9 --- .../references/composition-audit.md | 80 ++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/.agents/skills/skill-creator/references/composition-audit.md b/.agents/skills/skill-creator/references/composition-audit.md index 5666950de2..b17d906d2a 100644 --- a/.agents/skills/skill-creator/references/composition-audit.md +++ b/.agents/skills/skill-creator/references/composition-audit.md @@ -77,7 +77,7 @@ non-owners already delegate. A delegating repeat is resolved, not a smell. ### 3. Dead and orphan links ```bash -# broken reference links — match real markdown links ](...) only, keep the +# broken reference links: match real markdown links ](...) only, keep the # ../skill/ prefix, and resolve relative to the linking file's dir. Matching the # bare `references/x.md` tail (and dropping the prefix) gives false DEAD hits for # valid cross-skill links like ](../workspace-api/references/x.md) and for @@ -108,6 +108,82 @@ door). A `move` with 0 inbound is suspicious (nothing delegates to it: is its phrase real?). A `move` with exactly 1 inbound is tightly coupled (could it just be a section of that one caller?). Many inbound = a healthy shared move. +### 5. Description-restatement body section + +A full-library audit found ~40 skills opening with a "When to Apply This Skill" +list that restates the frontmatter description bullet for bullet. By the time +the body loads, routing already happened; the copy only costs tokens and drifts. + +```bash +grep -ln '^## When [Tt]o Apply' */SKILL.md +``` + +Open each hit and diff its bullets against the description. Pure restatement = +delete the section. Keep it only when it adds recognition cues the description +cannot carry (concrete code smells, near-miss boundaries). + +### 6. Restating closer + +A closing "Quick Reference", "Checklist", "Best Practices", "Common Gotchas", +"Anti-Patterns", or "Complete Example" that restates the body item for item. +Two copies of one rule drift to different calibrations (technical-articles +shipped "max 3-4 sentences" in the body and "4-5" in its closer). + +```bash +grep -nE '^#{2,3} (Quick Reference|.*Checklist|Best Practices|Anti-[Pp]atterns|Common (Gotchas|Mistakes|Pitfalls)|Complete .*Example|Final Check|What to Avoid)' */SKILL.md +``` + +For each closer item, find the section above that owns it; delete owned items +and keep only rules stated nowhere else. An exit gate that verifies earlier +artifacts (grep the debug prefix, re-run the repro) is not a restatement; keep it. + +### 7. Transcript and import residue + +Sections harvested from the one conversation (or upstream repo) that spawned +them: version literals, PR numbers, spec paths (specs are deleted when done, so +the links dangle by construction), "not yet"/"currently" state snapshots, and +other-framework examples in a Bun + Hono + Svelte repo. + +```bash +grep -rnE 'v[0-9]+\.[0-9]+\.[0-9]+|PR #[0-9]+|specs/[0-9]{8}T[0-9]{6}|not yet|[Cc]urrently' */SKILL.md +grep -rnE 'Next\.js|React Router|react-|from "react|Prisma' */SKILL.md +``` + +Version, PR, and spec hits inside teaching examples become placeholders or a +two-line "the pattern converged on X" summary. Framework hits are import +residue unless the skill is explicitly about that framework. + +### 8. Dash-strip damage + +A past mechanical em dash removal left floating colons (" : ") and glued text +("works:WHY", "):that") across 8+ skills, so the dash grep reads clean while +the prose is broken. + +```bash +grep -rnE ' : |\):[a-z]|[a-z]:[A-Z][A-Z]' */SKILL.md +``` + +Replace with a real colon, semicolon, comma, or sentence break per +writing-voice. Ternaries and conditional types in fenced code also match +" : ", so skim each hit's context before editing; only prose hits are +findings. + +### 9. Everyday-ask triggers + +A description claiming a phrase people say constantly in conversations that +should not load the skill ("be brief", "what should we do", "simplify this", +"can you summarize", "what does X do"). Worst when the skill is sticky or +heavyweight: caveman's persistent persona fired on a one-off "be brief". + +```bash +# scan the whole frontmatter: descriptions can be block scalars spanning lines +for d in */SKILL.md; do awk '/^---$/{n++} n==1' "$d"; done | grep -oE '"[^"]{2,40}"' | sort | uniq -c | sort -rn +``` + +For each quoted phrase ask: would this phrase occur in a conversation where +loading the skill is wrong? If yes, narrow the phrase ("summarize what we +did") or add a near-miss clause ("for a plain code question, answer directly"). + ## Judgment Grill After the mechanical pass, ask the skill-creator questions per skill, then two @@ -165,7 +241,7 @@ A self-paced loop invocation: ```txt /loop Audit the review/simplify skill cluster for composition health. -Load skill-creator and read references/composition-audit.md. Run detectors 1-4, +Load skill-creator and read references/composition-audit.md. Run detectors 1-9, then the judgment grill. Report findings in the output shape and fix only mechanical, grounded ones (collisions, dead links, copied bodies). Escalate ambiguous triggers to references/evaluation.md. Stop when a full pass finds From be97b021c9b333b69ba5015d5de04124aa756e8d Mon Sep 17 00:00:00 2001 From: Braden Wong <13159333+braden-w@users.noreply.github.com> Date: Wed, 1 Jul 2026 15:23:07 -0700 Subject: [PATCH 12/12] docs(govuk-style): narrow triggers to external-reader prose --- .agents/skills/govuk-style/SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.agents/skills/govuk-style/SKILL.md b/.agents/skills/govuk-style/SKILL.md index c97df39507..6023f94046 100644 --- a/.agents/skills/govuk-style/SKILL.md +++ b/.agents/skills/govuk-style/SKILL.md @@ -1,6 +1,6 @@ --- name: govuk-style -description: Write and edit in GOV.UK / GDS house style — plain English, active voice, front-loaded content, sentence case, and no bold or italics for emphasis. Use when writing or editing reports, research write-ups, guidance, documentation, summaries, or any prose where clarity and accessibility matter. +description: 'Write and edit in GOV.UK / GDS house style: plain English, active voice, front-loaded content, sentence case, and no bold or italics for emphasis. Use when the user asks for GOV.UK or GDS style, or when writing reports, research write-ups, or guidance for external readers. Not for repo docs, UI strings, commit messages, or code comments; those follow writing-voice.' user-invokable: true args: - name: target @@ -10,7 +10,7 @@ args: Open the content up so anyone can understand it the first time they read it — without losing any of the substance, nuance or precision. The goal is to open up, not to dumb down. This skill applies the GOV.UK style guide and the Government Digital Service (GDS) content design principles. It is based on the GOV.UK A to Z style guide and writing guidelines (guidance.publishing.service.gov.uk). -Apply it to reports, research write-ups, guidance and any prose meant to be read. When you write a report, default to this style. When you brief a research agent, pass this skill so its report follows the same style. +Apply it to reports, research write-ups and guidance written for external readers. When you write a report, default to this style. When you brief a research agent, pass this skill so its report follows the same style. Repo documentation, UI strings, commit messages and code comments follow writing-voice instead. ## Content design principles