diff --git a/.agents/skills/ask-matt/SKILL.md b/.agents/skills/ask-matt/SKILL.md index 7f3ab78..56bea5f 100644 --- a/.agents/skills/ask-matt/SKILL.md +++ b/.agents/skills/ask-matt/SKILL.md @@ -1,90 +1,28 @@ --- name: ask-matt -description: Ask which skill or flow fits your situation. A router over the skills in this repo. -disable-model-invocation: true +description: Recommend the repository skill or workflow that best fits the user's situation. --- # Ask Matt -You don't remember every skill, so ask. +Recommend the shortest useful route for the requested outcome. These are options, not a required pipeline. Reuse known decisions and existing configuration; do not start work, an interview, or publication merely because you recommended a skill. -A **flow** is a path through the skills. Most paths run along one **main flow**, and two **on-ramps** merge onto it. Everything else is standalone, or a vocabulary layer that runs underneath. +## Choose a route -## The main flow: idea → ship +- A clear spec ready to build: `/implement`. For explicitly test-first work, `/tdd`; for a review only, `/code-review`. +- An idea with consequential open questions: `/grill-with-docs` for repo work, or `/grill-me` for a standalone discussion. Skip interviews whose answers are already known. +- A design question that needs a runnable example: `/prototype`. Return to the original discussion with the verdict; production implementation is a separate choice. +- A large plan needing durable acceptance criteria: `/to-spec`, then `/to-tickets` if decomposition helps. Drafting, filing, and implementing remain distinct requested outcomes. +- A genuinely uncertain multi-session effort: `/wayfinder` for decision tickets. A well-scoped feature does not need a planning map. +- A reported failure: `/diagnosing-bugs` for evidence and cause, plus repair only when requested. +- Incoming reports to classify: `/triage`. An explicitly chosen issue can be reviewed regardless of who created it. -The route most work travels. You have an idea and want it built. +## Focused alternatives -1. **`/grill-with-docs`** — sharpen the idea by interview. Start here whenever you are **working in a working directory**: it's stateful, retaining what it learns in `CONTEXT.md` and ADRs. (No working directory? Use `/grill-me` — see Standalone. Both run the same `/grilling` primitive; `grill-with-docs` is the one that leaves a paper trail, which makes it the better of the two whenever a repo is there to leave it in.) -2. **Branch — can you settle every question in conversation?** If a question needs a runnable answer (state, business logic, a UI you have to see), detour through a prototype, bridged by **`/handoff`** in both directions (a prototype lives in its own directory, which is exactly what `/handoff` is for — see Phase boundaries): - - **`/handoff`** out, then open a fresh session against that file, - - **`/prototype`** to answer the question with throwaway code, - - **`/handoff`** back what you learned, and reference it from the original idea thread. -3. **Branch — is this a multi-session build?** - - **Yes** → **`/to-spec`** (turn the thread into a spec), then **`/to-tickets`** to split it into tracer-bullet tickets, each declaring its **blocking edges**. On a local tracker that's one file per ticket under `.scratch//issues/`, worked blockers-first by hand; on a real tracker the edges become native blocking links, so any ticket whose blockers are done can be grabbed — kick off **`/implement`** per ticket, **`/clear`ing context between each one**. Each ticket is self-contained, so the last one's context is disposable. - - **No** → **`/implement`** right here, in the same context window. +Use `/domain-modeling` for project terminology or ADRs, `/codebase-design` for module shape, and `/improve-codebase-architecture` for a requested architecture survey. `/research` gathers primary-source evidence; `/teach` explains or practices a topic; `/writing-great-skills` helps maintain skills. - Either way, **`/implement`** builds each issue by driving **`/tdd`** internally — one red-green slice at a time — then closes out by running **`/code-review`**, a two-axis review (Standards + Spec) of the diff, before committing. Reach for **`/tdd`** on its own when you just want to build a concrete behaviour test-first without a full spec, and **`/code-review`** on its own whenever you want to review a branch or PR against a fixed point. +Use `/handoff` when a fresh session or durable summary is useful. Do not mandate context resets, prevent normal compaction, or assume a universal token threshold. -### Context hygiene +Read existing `docs/agents/` configuration when a workflow needs it. Recommend `/setup-matt-pocock-skills` only for missing or explicitly changed configuration, not before every engineering task. -Keep steps 1–3 in **one unbroken context window** — don't compact or clear until after `/to-tickets` — so the grilling, spec, and tickets all build on the same thinking. Each `/implement` then starts fresh, working from the ticket. - -The limit on this is the **[smart zone](https://www.aihero.dev/ai-coding-dictionary/smart-zone)**: the window (~150k tokens on state-of-the-art models) within which the model still reasons sharply. If a session approaches it before `/to-tickets`, don't push on degraded — `/compact` at the nearest phase boundary and carry on (see Phase boundaries). - -## On-ramps - -A starting situation that generates work, then merges onto the main flow. - -- **Bugs and requests piling up** → **`/triage`**. It moves issues through triage roles and produces agent-ready issues, which **`/implement`** later picks up. - - Triage is only for issues **you didn't create** — bug reports, incoming feature requests, anything that arrives raw. Tickets that `/to-tickets` produced are already agent-ready, so **don't triage them**. - -- **Something's broken** → **`/diagnosing-bugs`**. For the hard ones: the bug that resists a first glance, the intermittent flake, the regression that crept in between two known-good states. It refuses to theorise until it has a **tight feedback loop** — one command that already goes red on *this* bug — then fixes with a regression test. Its post-mortem hands off to **`/improve-codebase-architecture`** when the real finding is that there's no good seam to lock the bug down. - -- **A huge, foggy effort — a greenfield project or a huge feature build, too big for one session** → **`/wayfinder`**, the most cognitively demanding flow here. When the way from here to the destination isn't visible yet, it charts a **shared map** of **decision tickets** on the issue tracker and resolves them one at a time — producing **decisions, not deliverables** — until the fog is pushed back and the way is clear. Where **`/grill-with-docs`** sharpens an idea you can hold in one session, wayfinder is for the idea you can't — and it's slower and denser, so save it for exactly that, never a well-scoped feature. - - When the map clears, **it hands off, it doesn't build**: merge onto the main flow at **`/to-spec`**, which collapses the map's linked decisions into a buildable plan, then `/to-tickets` and `/implement` as usual. Looping the map straight into `/implement` skips that collapse and throws the linked detail away — go straight to `/implement` only when the effort turned out genuinely small. - -## Codebase health - -Not feature work — upkeep. - -- **`/improve-codebase-architecture`** — run whenever you have a spare moment to keep the codebase good for agents to operate in. It surfaces **deepening opportunities**; picking one _generates an idea_ you can take into the main flow at `/grill-with-docs`. It's the survey that finds the candidates; **`/codebase-design`** (below) is the bench you design the chosen one on. - -## Vocabulary underneath - -Two model-invoked references that run *beneath* the other skills — each the single source of truth for its vocabulary. Reach for them directly when the **words**, not the process, are the problem; or let the skills above pull them in. - -- **`/domain-modeling`** — sharpen the project's *domain* language: challenge a fuzzy term, resolve an overloaded word ("account" doing three jobs), record a hard-to-reverse decision as an ADR. It's the active discipline `/grill-with-docs` drives to keep `CONTEXT.md` a clean glossary. -- **`/codebase-design`** — the deep-module vocabulary (module, interface, depth, seam, adapter, leverage, locality) for designing a module's *shape*: a lot of behaviour behind a small interface at a clean seam. `/tdd` and `/improve-codebase-architecture` both speak it. - -## Phase boundaries - -A **phase** is a chunk of work inside a session — the grilling, the implementation, the QA. At the **boundary** between two of them you have five options, and picking between them is the fuzziest decision in this whole map: - -- **Continue** — stay put. Costs nothing, loses nothing. -- **`/clear`** — empty the window, when nothing here matters to what's next. -- **`/handoff`** — write a portable markdown file. Narrow: only for a **new harness**, a **new directory**, a **colleague**, or forking a side task **mid-phase**. What it buys is portability. -- **Subagent** — send a tightly-scoped task to its own window and get a report back. -- **`/compact`** — compress this context and seed a fresh session with it. The **default**, at the bottom of the tree rather than the first reach. - -Read [PHASE-BOUNDARIES.md](PHASE-BOUNDARIES.md) for the ordered tree — the five questions, the reasoning behind each branch, and why the primary-source cost makes **Continue** the one to rule out first. Make the decision **at** a boundary; mid-phase, continue or split the rest into subagents. - -## Standalone - -Off the main flow entirely. - -- **`/grill-me`** — the same relentless interview as `/grill-with-docs`, but **stateless**: it saves nothing locally and builds no `CONTEXT.md`. Reach for it when you are **not working in a working directory** — sharpening a plan, a design, a piece of writing, anything with no repo under it. If you are in a working directory, use `/grill-with-docs` instead: it runs the same interview and leaves a paper trail, so it is strictly the better one. -- **`/grilling`** — the interview primitive itself: rounds, the frontier, facts are the agent's job and decisions are yours. `/grill-me` and `/grill-with-docs` are the two named ways in, and `/triage`, `/wayfinder` and `/improve-codebase-architecture` all run it internally. Reach for it directly only when you want the interview with no wrapper around it. -- **`/resolving-merge-conflicts`** — work an in-progress merge or rebase conflict hunk by hunk, resolving by **intent** traced to each side's primary source rather than by picking lines, then finish the operation. It never runs `--abort`. Standalone and off every flow: reach for it when you are already mid-conflict. -- **`/prototype`** — a small, throwaway program that answers one design question: does this state model feel right, or what should this UI look like. Throwaway is a constraint on how the code is written, not a promise to destroy it: the answer folds into the real code, and the prototype itself is kept as a **primary source** on a `prototype/` branch out of main, pointed at from the implementation issue. It's the detour in step 2 of the main flow, but reach for it any time a design question is hard to settle on paper. -- **`/research`** — delegate reading legwork to a **background agent**: it investigates a question against **primary sources**, then leaves a cited Markdown file in the repo. Keep working while it reads. The file it produces is something to take *into* the main flow at `/grill-with-docs` — research feeds the thinking, it doesn't replace it. -- **`/to-questionnaire`** — when the thing blocking you isn't in your head or the codebase but in **someone else's**, this writes them a questionnaire to fill in. It's the inverse of `/grill-me`: instead of interviewing you about the subject, it interviews you about the **send** — who it's going to, what you need back — and aims the questions at the gap. What comes back is material for `/grill-with-docs` or `/to-spec`. -- **`/wizard`** — for the steps only a **human** can take: provisioning infrastructure, setting up credentials or CI secrets, clicking through an unfamiliar third-party dashboard, running a one-off migration or cutover. It generates an interactive bash script that opens each URL, captures each value, and writes it into `.env` and GitHub secrets — so the procedure stops being something you re-explain to an agent every time. Model-invoked, so the agent reaches for it the moment it hits a wall only you can pass. If the agent could just do it itself, it should; this is for where a human is genuinely in the loop. -- **`/wait-what`** — the corrective for a message that didn't land. Use it mid-conversation, inside any other skill, and the agent re-pitches what it just said with the context you were missing, in plain English, using the `CONTEXT.md` vocabulary. It works after the fact; `/grill-with-docs` is the upfront cure, because a shared language agreed early is what stops the jargon arriving at all. -- **`/teach`** — learn a concept over multiple sessions, using the current directory as a stateful workspace. -- **`/writing-for-agents`** — reference for writing documents agents consume: skills, AGENTS.md, pointed-at docs. - -## Precondition - -**`/setup-matt-pocock-skills`** — run before your first engineering flow to configure the issue tracker, triage labels, and doc layout the other skills assume. Custom issue trackers also work. +Explain the recommended route briefly, including any prerequisite that actually matters. Honor the user's selected skill even when another route would also work. diff --git a/.agents/skills/claude-handoff/SKILL.md b/.agents/skills/claude-handoff/SKILL.md new file mode 100644 index 0000000..0c04384 --- /dev/null +++ b/.agents/skills/claude-handoff/SKILL.md @@ -0,0 +1,18 @@ +--- +name: claude-handoff +description: Hand the current conversation off to a background Claude Code session when the user chooses Claude for continuation. +--- + +Check `command -v claude` and the installed CLI's help for background-session support before launching. If unavailable, return the redacted handoff and explain the missing prerequisite; do not install software or switch agent products without the user's request. + +Write a handoff summary so a fresh Claude session can continue the work. When supported by the installed CLI, launch it with `claude --bg --name "" ""` in the selected working directory. Use the CLI's reported session identifier and management commands; do not claim a session started if the command failed. Do not retry an ambiguous launch until checking whether it already created a session. + +Always pass `-n`/`--name` with a descriptive name (e.g. `--name "Fix login bug"`) — it sets the display name shown in the job list, session picker, and terminal title. + +Include a "suggested skills" section in the summary, which suggests skills that the agent should invoke. + +Do not duplicate content already captured in other artifacts (PRDs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead. + +Redact any sensitive information, such as API keys, passwords, or personally identifiable information — the summary becomes the agent's prompt. + +If the user passed arguments, treat them as a description of what the next session will focus on and tailor the summary accordingly. diff --git a/.agents/skills/claude-handoff/agents/openai.yaml b/.agents/skills/claude-handoff/agents/openai.yaml new file mode 100644 index 0000000..0a7aa5d --- /dev/null +++ b/.agents/skills/claude-handoff/agents/openai.yaml @@ -0,0 +1,5 @@ +interface: + display_name: "Claude Handoff" + short_description: "Hand off to a background agent" +policy: + allow_implicit_invocation: false diff --git a/.agents/skills/code-review/SKILL.md b/.agents/skills/code-review/SKILL.md index 2d276fe..8979b06 100644 --- a/.agents/skills/code-review/SKILL.md +++ b/.agents/skills/code-review/SKILL.md @@ -1,35 +1,43 @@ --- name: code-review -description: Review the changes since a fixed point (commit, branch, tag, or merge-base) along two axes — Standards (does the code follow this repo's documented coding standards?) and Spec (does the code match what the originating issue/spec asked for?). Runs both reviews in parallel sub-agents and reports them side by side. Use when the user wants to review a branch, a PR, work-in-progress changes, or asks to "review since X". +description: Review the changes since a fixed point (commit, branch, tag, or merge-base) along two axes — Standards (does the code follow this repo's documented coding standards?) and Spec (does the code match what the originating issue/PRD asked for?). Runs both reviews in parallel sub-agents and reports them side by side. Use when the user wants to review a branch, a PR, work-in-progress changes, or asks to "review since X". --- -Two-axis review of the diff between `HEAD` and a fixed point the user supplies: +Two-axis review of the changes the user selected, including working changes when requested: - **Standards** — does the code conform to this repo's documented coding standards? -- **Spec** — does the code faithfully implement the originating issue / spec? +- **Spec** — does the code faithfully implement the originating issue / PRD / spec? -Both axes run as **parallel sub-agents** so they don't pollute each other's context, then this skill aggregates their findings. +Use independent **parallel sub-agents** when available, then aggregate their findings. If delegation is unavailable, review both axes directly and disclose the limitation. -The issue tracker should have been provided to you — run `/setup-matt-pocock-skills` if `docs/agents/issue-tracker.md` is missing. +Use `docs/agents/issue-tracker.md` for remote specs. A missing tracker configuration does not block reviewing local changes or a supplied spec. ## Process -### 1. Pin the fixed point +### 1. Pin the requested scope -Whatever the user said is the fixed point — a commit SHA, branch name, tag, `main`, `HEAD~5`, etc. If they didn't specify one, ask for it. +Honor the user's baseline and file scope. For a pre-commit review, use the implementation's recorded starting commit and task-owned paths; for an ordinary review of uncommitted work, default to `HEAD`. Ask only when an unresolved baseline or scope choice would materially change the review. -Capture the diff command once: `git diff ...HEAD` (three-dot, so the comparison is against the merge-base). Also note the list of commits via `git log ..HEAD --oneline`. +Resolve the baseline to a commit with `git rev-parse --verify '^{commit}'`. For a branch/PR review, use its merge-base when the request is for changes introduced on that branch; for an explicit commit-to-commit comparison, use the supplied commit directly. -Before going further, confirm the fixed point resolves (`git rev-parse `) and the diff is non-empty. A bad ref or empty diff should fail here — not inside two parallel sub-agents. +Choose and record commands appropriate to the selected scope, adding `-- ` where needed: + +| Scope | Tracked changes | New files | +| --- | --- | --- | +| Committed only | `git diff HEAD` | Included if committed | +| Current working result, including staged and unstaged | `git diff ` | List with `git ls-files --others --exclude-standard` and read each in-scope file | +| Staged only | `git diff --cached ` | Included if staged | + +Inspect `git status --short` and distinguish pre-existing user changes from task changes. When ownership overlaps, obtain the task's actual patch or state the limitation; do not attribute the entire dirty checkout to the implementation. Record relevant commits with `git log ..HEAD --oneline`. An empty tracked diff is not an empty review until in-scope new files are checked. Report genuinely empty scope directly; resolve invalid refs before dispatch. ### 2. Identify the spec source Look for the originating spec, in this order: -1. Issue references in the commit messages (`#123`, `Closes #45`, GitLab `!67`, etc.) — fetch via the workflow in `docs/agents/issue-tracker.md`. -2. A path the user passed as an argument. -3. A spec file under `docs/`, `specs/`, or `.scratch/` matching the branch name or feature. -4. If nothing is found, ask the user where the spec is. If they say there isn't one, the **Spec** sub-agent will skip and report "no spec available". +1. The user's explicit spec, ticket, path, or acceptance criteria in the conversation. Preserve it as authoritative even when commits reference other work. +2. Issue/MR references in relevant commit messages — fetch via `docs/agents/issue-tracker.md` when no explicit source was supplied. +3. A PRD/spec file under `docs/`, `specs/`, or `.scratch/` matching the branch name or feature. +4. If nothing is found or the source is inaccessible, continue the Standards review and report the Spec coverage limitation. Ask for a source only when needed to answer the user's request; do not invent requirements or require tracker setup. ### 3. Identify the standards sources @@ -57,15 +65,17 @@ Each smell reads *what it is* → *how to fix*; match it against the diff: ### 4. Spawn both sub-agents in parallel +Use the environment's available delegation tool; do not depend on a product-specific `Agent` or `Task` name. Give each reviewer the same resolved baseline, tracked diff commands, in-scope new-file list, task ownership boundaries, and current snapshot. Review-only agents must not modify files or external state. + **Standards sub-agent prompt** — include: -- The full diff command and commit list. +- The full diff commands, new-file contents or paths, scope, and commit list. - The list of standards-source files you found in step 3, **plus the smell baseline from step 3** pasted in full — the sub-agent has no other access to it. - The brief: "Report — per file/hunk where relevant — (a) every place the diff violates a documented standard: cite the standard (file + the rule); and (b) any baseline smell you spot: name it and quote the hunk. Distinguish hard violations from judgement calls — documented-standard breaches can be hard, but baseline smells are always judgement calls, and a documented repo standard overrides the baseline. Skip anything tooling enforces. Under 400 words." **Spec sub-agent prompt** — include: -- The diff command and commit list. +- The same diff commands, new files, scope, and commit list as the Standards reviewer. - The path or fetched contents of the spec. - The brief: "Report: (a) requirements the spec asked for that are missing or partial; (b) behaviour in the diff that wasn't asked for (scope creep); (c) requirements that look implemented but where the implementation looks wrong. Quote the spec line for each finding. Under 400 words." diff --git a/.agents/skills/codebase-design/DEEPENING.md b/.agents/skills/codebase-design/DEEPENING.md index 3938457..955aadb 100644 --- a/.agents/skills/codebase-design/DEEPENING.md +++ b/.agents/skills/codebase-design/DEEPENING.md @@ -8,7 +8,7 @@ When assessing a candidate for deepening, classify its dependencies. The categor ### 1. In-process -Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed. +Pure computation, in-memory state, no I/O. Merge only when it concentrates a cohesive responsibility and reduces caller complexity. Test through the resulting behavior-level interface; no adapter is needed for pure computation. ### 2. Local-substitutable @@ -26,12 +26,12 @@ Third-party services (Stripe, Twilio, etc.) you don't control. The deepened modu ## Seam discipline -- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection. +- **Justify a seam by its current benefit.** Test isolation, ownership, real variability, or an external contract can justify it; adapter count alone cannot. Do not manufacture a second implementation or an unused port. - **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them. -## Testing strategy: replace, don't layer +## Testing strategy: preserve behavioral coverage -- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them. +- Before deleting old tests, map their success, failure, and edge-case coverage to the replacement checks. Delete only redundant or obsolete tests after confirming equivalent meaningful coverage; keep tests that catch distinct failures. - Write new tests at the deepened module's interface. The **interface is the test surface**. - Tests assert on observable outcomes through the interface, not internal state. -- Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface. +- Prefer tests that survive behavior-preserving refactors. A test change is a signal to review coupling, not proof that the test is worthless; integration, algorithm, and concurrency checks may protect distinct contracts. diff --git a/.agents/skills/codebase-design/DESIGN-IT-TWICE.md b/.agents/skills/codebase-design/DESIGN-IT-TWICE.md index 8419ad6..668c228 100644 --- a/.agents/skills/codebase-design/DESIGN-IT-TWICE.md +++ b/.agents/skills/codebase-design/DESIGN-IT-TWICE.md @@ -18,14 +18,13 @@ Show this to the user, then immediately proceed to Step 2. The user reads and th ### 2. Spawn sub-agents -Spawn 3+ sub-agents in parallel. Each must produce a **radically different** interface for the deepened module. +Honor the requested number of alternatives; two is a useful default. Use independent subagents through the available delegation tool when useful and within available slots, or compare directly when delegation is unavailable. Each option should explore a meaningfully different trade-off for the same requirements. Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint: -- Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point." -- Agent 2: "Maximise flexibility — support many use cases and extension." -- Agent 3: "Optimise for the most common caller — make the default case trivial." -- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies." +- One option can optimize for the primary caller's workflow. +- Another can emphasize explicit state transitions or failure handling. +- Add a dependency-focused option only when that is an actual design question; do not invent flexibility, adapters, or a target method count. Include both [SKILL.md](SKILL.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language. @@ -39,6 +38,6 @@ Each sub-agent outputs: ### 3. Present and compare -Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**. +Use the requested or clearest concise format. Contrast **depth** (leverage at the interface), **locality** (where change concentrates), **seam placement**, implementation effort, and risk. After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the user wants a strong read, not a menu. diff --git a/.agents/skills/codebase-design/SKILL.md b/.agents/skills/codebase-design/SKILL.md index 16620c2..609c02c 100644 --- a/.agents/skills/codebase-design/SKILL.md +++ b/.agents/skills/codebase-design/SKILL.md @@ -9,17 +9,17 @@ Design **deep modules**: a lot of behaviour behind a small interface, placed at ## Glossary -Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point. +Use these concepts to explain the design, while preserving the repository's actual names and domain vocabulary. A component, service, or API can be the module or interface under discussion; explain the distinction when it matters instead of banning familiar terms. -**Module** — anything with an interface and an implementation. Deliberately scale-agnostic: a function, class, package, or tier-spanning slice. _Avoid_: unit, component, service. +**Module** — anything with an interface and an implementation: a function, component, service, class, package, or tier-spanning slice. -**Interface** — everything a caller must know to use the module correctly: the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. _Avoid_: API, signature (too narrow — they refer only to the type-level surface). +**Interface** — everything a caller must know: signatures, invariants, ordering, errors, configuration, and relevant performance characteristics. An API signature alone may describe only part of that contract. **Implementation** — what's inside a module, its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise. **Depth** — leverage at the interface: the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface, **shallow** when the interface is nearly as complex as the implementation. -**Seam** _(Michael Feathers)_ — a place where you can alter behaviour without editing in that place; the *location* at which a module's interface lives. Where to put the seam is its own design decision, distinct from what goes behind it. _Avoid_: boundary (overloaded with DDD's bounded context). +**Seam** _(Michael Feathers)_ — a place where behavior can be varied without editing that place. Distinguish a test or substitution seam from a domain or deployment boundary when the difference affects the design. **Adapter** — a concrete thing that satisfies an interface at a seam. Describes *role* (what slot it fills), not substance (what's inside). @@ -61,8 +61,8 @@ When designing an interface, ask: - **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface. - **The deletion test.** Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep. -- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test *past* the interface, the module is probably the wrong shape. -- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it. +- **Prefer behavior-level test surfaces.** Test through the relevant contract, while retaining focused checks for internal algorithms or integration invariants when they catch failures the higher interface does not. +- **Justify seams by a current need.** Test isolation, ownership, external integration, or real variation may justify a boundary even with one runtime implementation. Do not add an adapter merely to satisfy a count, or a speculative abstraction with no concrete benefit. ## Designing for testability @@ -80,7 +80,7 @@ Good interfaces make testing natural: } ``` -2. **Return results, don't produce side effects.** +2. **Prefer pure results for calculations; keep necessary effects explicit.** ```typescript // Testable @@ -106,7 +106,7 @@ Good interfaces make testing natural: - **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead. - **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know. -- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**. +- **Unqualified boundaries**: clarify whether a boundary concerns tests, domain ownership, deployment, or caller contracts when that distinction matters. ## Going deeper diff --git a/.agents/skills/design-an-interface/SKILL.md b/.agents/skills/design-an-interface/SKILL.md new file mode 100644 index 0000000..4928a92 --- /dev/null +++ b/.agents/skills/design-an-interface/SKILL.md @@ -0,0 +1,94 @@ +--- +name: design-an-interface +description: Generate multiple radically different interface designs for a module using parallel sub-agents. Use when user wants to design an API, explore interface options, compare module shapes, or mentions "design it twice". +--- + +# Design an Interface + +Based on "Design It Twice" from "A Philosophy of Software Design": your first idea is unlikely to be the best. Generate multiple radically different designs, then compare. + +## Workflow + +### 1. Gather Requirements + +Before designing, understand: + +- [ ] What problem does this module solve? +- [ ] Who are the callers? (other modules, external users, tests) +- [ ] What are the key operations? +- [ ] Any constraints? (performance, compatibility, existing patterns) +- [ ] What should be hidden inside vs exposed? + +Infer these from the request and existing callers; ask only about missing requirements that materially affect the designs. + +### 2. Generate Designs (Parallel Sub-Agents) + +Compare meaningfully different approaches, honoring the requested count. Two is a useful default for "design it twice"; add options only when they explore a relevant trade-off. Use independent subagents through the available delegation tool when useful and within available slots; otherwise develop the alternatives directly. + +``` +Prompt template for each sub-agent: + +Design an interface for: [module description] + +Requirements: [gathered requirements] + +Constraints for this design: [assign a different constraint to each agent] +- Option A: "Optimize for the primary caller's workflow" +- Option B: "Expose explicit state transitions for the same requirements" + +Choose constraints relevant to the actual problem; do not invent future use cases or a target method count. + +Output format: +1. Interface signature (types/methods) +2. Usage example (how caller uses it) +3. What this design hides internally +4. Trade-offs of this approach +``` + +### 3. Present Designs + +Show each design with: + +1. **Interface signature** - types, methods, params +2. **Usage examples** - how callers actually use it in practice +3. **What it hides** - complexity kept internal + +Present designs sequentially so user can absorb each approach before comparison. + +### 4. Compare Designs + +After showing all designs, compare them on: + +- **Interface simplicity**: fewer methods, simpler params +- **General-purpose vs specialized**: flexibility vs focus +- **Implementation efficiency**: does shape allow efficient internals? +- **Depth**: small interface hiding significant complexity (good) vs large interface with thin implementation (bad) +- **Ease of correct use** vs **ease of misuse** + +Use the requested format or the clearest concise comparison. Highlight where designs diverge most. + +### 5. Synthesize + +Recommend the best fit with reasons. If the user wants to choose interactively, ask: + +- "Which design best fits your primary use case?" +- "Any elements from other designs worth incorporating?" + +## Evaluation Criteria + +From "A Philosophy of Software Design": + +**Interface simplicity**: Fewer methods, simpler params = easier to learn and use correctly. + +**Scope fit**: Covers known callers and requirements without speculative flexibility. + +**Implementation efficiency**: Does interface shape allow efficient implementation? Or force awkward internals? + +**Depth**: Small interface hiding significant complexity = deep module (good). Large interface with thin implementation = shallow module (avoid). + +## Anti-Patterns + +- Don't let sub-agents produce similar designs - enforce radical difference +- Don't skip comparison - the value is in contrast +- Don't implement - this is purely about interface shape +- Include implementation effort and migration risk in the trade-offs without letting them obscure correctness or usability. diff --git a/.agents/skills/design-an-interface/agents/openai.yaml b/.agents/skills/design-an-interface/agents/openai.yaml new file mode 100644 index 0000000..c86df18 --- /dev/null +++ b/.agents/skills/design-an-interface/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Design an Interface" + short_description: "Explore alternative module interfaces" diff --git a/.agents/skills/diagnosing-bugs/SKILL.md b/.agents/skills/diagnosing-bugs/SKILL.md index 7f8acf7..0ecf7d5 100644 --- a/.agents/skills/diagnosing-bugs/SKILL.md +++ b/.agents/skills/diagnosing-bugs/SKILL.md @@ -1,140 +1,40 @@ --- name: diagnosing-bugs -description: Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow. +description: Investigate bugs and performance regressions to explain their cause with evidence. Use for diagnosis or debugging; implement repairs only when fixing is also requested. --- # Diagnosing Bugs -A discipline for hard bugs. Skip phases only when explicitly justified. +Read the relevant flow, existing tests, `CONTEXT.md`, and applicable ADRs. Establish the requested outcome: diagnosis ends with a cause and evidence; an authorized fix also includes repair and regression checks. Keep all investigation within permitted systems and preserve unrelated work. -When exploring the codebase, read `CONTEXT.md` (if it exists) to get a clear mental model of the relevant modules, and check ADRs in the area you're touching. +## Find a useful signal -## Redact +Start from the reported symptom and what is already known. Read code, logs, and recent changes to form hypotheses; do not require a running reproduction before inspecting the code. -This skill has you show commands, outputs and captured artifacts. **Redact every secret first** — write `` in its place. Build loops against env vars, so the credential stays in the environment rather than in what you show. Captured artifacts carry auth headers: quote only the lines that carry the signal. +Prefer the smallest reliable reproduction: an existing failing test, a scoped command with dummy input, a local request, or a browser check. Choose a harness only if existing checks cannot reach the failure. When a human must perform the trigger, adapt [the HITL loop template](scripts/hitl-loop.template.sh); do not run it unattended. -If the redacted output is not enough to diagnose the bug, say so and ask the user. +Capture the exact error, wrong result, or measured regression. Distinguish the reported bug from nearby failures. Minimize inputs when it helps separate causes; perfect minimization and second-long execution are not prerequisites. -## Phase 1 — Build a feedback loop +For intermittent bugs, record attempts, failures, and environmental conditions. Use bounded stress or repeated runs when safe; do not overload shared systems. A low reproduction rate is evidence, not a reason to abandon inspection. -**This is the skill.** Everything else is mechanical. If you have a **tight** pass/fail signal for the bug — one that goes red on _this_ bug — you will find the cause; bisection, hypothesis-testing, and instrumentation all just consume it. If you don't have one, no amount of staring at code will save you. +If execution is unavailable, continue safe code/trace analysis, distinguish a probable cause from a confirmed one, and state what would confirm it. Request only the missing artifact or access that materially limits progress. Production instrumentation requires explicit authority. -Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.** +## Test explanations -### Ways to construct one — try them in roughly this order +Rank plausible hypotheses by evidence and state a prediction for each. One well-supported explanation may be enough; compare alternatives when uncertainty warrants it. -1. **Failing test** at whatever seam reaches the bug — unit, integration, e2e. -2. **Curl / HTTP script** against a running dev server. -3. **CLI invocation** with a fixture input, diffing stdout against a known-good snapshot. -4. **Headless browser script** (Playwright / Puppeteer) — drives the UI, asserts on DOM/console/network. -5. **Replay a captured trace.** Save a real network request / payload / event log to disk; replay it through the code path in isolation. -6. **Throwaway harness.** Spin up a minimal subset of the system (one service, mocked deps) that exercises the bug code path with a single function call. -7. **Property / fuzz loop.** If the bug is "sometimes wrong output", run 1000 random inputs and look for the failure mode. -8. **Bisection harness.** If the bug appeared between two known states (commit, dataset, version), automate "boot at state X, check, repeat" so you can `git bisect run` it. -9. **Differential loop.** Run the same input through old-version vs new-version (or two configs) and diff outputs. -10. **HITL bash script.** Last resort. If a human must click, drive _them_ with `scripts/hitl-loop.template.sh` so the loop is still structured. Captured output feeds back to you. +Use targeted inspection or probes to distinguish predictions. Change one relevant variable at a time where practical. Tag temporary instrumentation with a task-unique prefix, avoid sensitive payloads, and keep track of exactly what you added. -Build the right feedback loop, and the bug is 90% fixed. +For performance, measure the same workload against a baseline before proposing an optimization; account for warmup and noise. Use profiling, query plans, or bisection when they help isolate the cause. Keep bisection out of a shared dirty worktree. -### Tighten the loop +## Repair only when requested -Treat the loop as a product. Once you have _a_ loop, **tighten** it: +For an authorized fix, turn the reproduction into a regression check before the repair where feasible. Exercise the real failing caller pattern: a single-caller unit test cannot prove a multi-caller race is fixed. -- Can I make it faster? (Cache setup, skip unrelated init, narrow the test scope.) -- Can I make the signal sharper? (Assert on the specific symptom, not "didn't crash".) -- Can I make it more deterministic? (Pin time, seed RNG, isolate filesystem, freeze network.) +Apply the smallest root-cause correction, rerun the original scenario, then relevant surrounding tests. If no reliable check is available, state the resulting verification gap rather than claiming success from an unrelated green test. -A 30-second flaky loop is barely better than no loop; a 2-second deterministic one is tight — a debugging superpower. +## Hand off -### Non-deterministic bugs +Remove only your own temporary probes and disposable artifacts. Report the cause, supporting evidence, checks run, and remaining uncertainty. For a fix, also report the changed behavior and regression result. -The goal is not a clean repro but a **higher reproduction rate**. Loop the trigger 100×, parallelise, add stress, narrow timing windows, inject sleeps. A 50%-flake bug is debuggable; 1% is not — keep raising the rate until it's debuggable. - -### When you genuinely cannot build a loop - -Stop and say so explicitly. List what you tried. Ask the user for: (a) access to whatever environment reproduces it, (b) a redacted captured artifact (HAR file, log dump, core dump, screen recording with timestamps), or (c) permission to add temporary production instrumentation. Do **not** proceed to hypothesise without a loop. - -### Completion criterion — a tight loop that goes red - -Phase 1 is done when the loop is **tight** and **red-capable**: you can name **one command** — a script path, a test invocation, a curl — that you have **already run at least once** (show the invocation and its output, redacted), and that is: - -- [ ] **Red-capable** — it drives the actual bug code path and asserts the **user's exact symptom**, so it can go red on this bug and green once fixed. Not "runs without erroring" — it must be able to _catch this specific bug_. -- [ ] **Deterministic** — same verdict every run (flaky bugs: a pinned, high reproduction rate, per above). -- [ ] **Fast** — seconds, not minutes. -- [ ] **Agent-runnable** — you can run it unattended; a human in the loop only via `scripts/hitl-loop.template.sh`. - -If you catch yourself reading code to build a theory before this command exists, **stop — jumping straight to a hypothesis is the exact failure this skill prevents.** No red-capable command, no Phase 2. - -## Phase 2 — Reproduce + minimise - -Run the loop. Watch it go red — the bug appears. - -Confirm: - -- [ ] The loop produces the failure mode the **user** described — not a different failure that happens to be nearby. Wrong bug = wrong fix. -- [ ] The failure is reproducible across multiple runs (or, for non-deterministic bugs, reproducible at a high enough rate to debug against). -- [ ] You have captured the exact symptom (error message, wrong output, slow timing) so later phases can verify the fix actually addresses it. - -### Minimise - -Once it's red, shrink the repro to the **smallest scenario that still goes red**. Cut inputs, callers, config, data, and steps **one at a time**, re-running the loop after each cut — keep only what's load-bearing for the failure. - -Why bother: a minimal repro shrinks the hypothesis space in Phase 3 (fewer moving parts left to suspect) and becomes the clean regression test in Phase 5. - -Done when **every remaining element is load-bearing** — removing any one of them makes the loop go green. - -Do not proceed until you have reproduced **and** minimised. - -## Phase 3 — Hypothesise - -Generate **3–5 ranked hypotheses** before testing any of them. Single-hypothesis generation anchors on the first plausible idea. - -Each hypothesis must be **falsifiable**: state the prediction it makes. - -> Format: "If is the cause, then will make the bug disappear / will make it worse." - -If you cannot state the prediction, the hypothesis is a vibe — discard or sharpen it. - -**Show the ranked list to the user before testing.** They often have domain knowledge that re-ranks instantly ("we just deployed a change to #3"), or know hypotheses they've already ruled out. Cheap checkpoint, big time saver. Don't block on it — proceed with your ranking if the user is AFK. - -## Phase 4 — Instrument - -Each probe must map to a specific prediction from Phase 3. **Change one variable at a time.** - -Tool preference: - -1. **Debugger / REPL inspection** if the env supports it. One breakpoint beats ten logs. -2. **Targeted logs** at the boundaries that distinguish hypotheses. -3. Never "log everything and grep". - -**Tag every debug log** with a unique prefix, e.g. `[DEBUG-a4f2]`. Cleanup at the end becomes a single grep. Untagged logs survive; tagged logs die. - -**Perf branch.** For performance regressions, logs are usually wrong. Instead: establish a baseline measurement (timing harness, `performance.now()`, profiler, query plan), then bisect. Measure first, fix second. - -## Phase 5 — Fix + regression test - -Write the regression test **before the fix** — but only if there is a **correct seam** for it. - -A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site. If the only available seam is too shallow (single-caller test when the bug needs multiple callers, unit test that can't replicate the chain that triggered the bug), a regression test there gives false confidence. - -**If no correct seam exists, that itself is the finding.** Note it. The codebase architecture is preventing the bug from being locked down. Flag this for the next phase. - -If a correct seam exists: - -1. Turn the minimised repro into a failing test at that seam. -2. Watch it fail. -3. Apply the fix. -4. Watch it pass. -5. Re-run the Phase 1 feedback loop against the original (un-minimised) scenario. - -## Phase 6 — Cleanup + post-mortem - -Required before declaring done: - -- [ ] Original repro no longer reproduces (re-run the Phase 1 loop) -- [ ] Regression test passes (or absence of seam is documented) -- [ ] All `[DEBUG-...]` instrumentation removed (`grep` the prefix) -- [ ] Throwaway prototypes deleted (or moved to a clearly-marked debug location) -- [ ] The hypothesis that turned out correct is stated in the commit / PR message — so the next debugger learns - -**Then ask: what would have prevented this bug?** If the answer involves architectural change (no good test seam, tangled callers, hidden coupling) hand off to the `/improve-codebase-architecture` skill with the specifics. Make the recommendation **after** the fix is in, not before — you have more information now than when you started. +Mention a broader architecture issue only when the evidence supports it; do not launch another audit or refactor unless requested or necessary for the authorized repair. diff --git a/.agents/skills/diagnosing-bugs/scripts/hitl-loop.template.sh b/.agents/skills/diagnosing-bugs/scripts/hitl-loop.template.sh index 43daedd..40afc46 100644 --- a/.agents/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +++ b/.agents/skills/diagnosing-bugs/scripts/hitl-loop.template.sh @@ -11,9 +11,6 @@ # capture VAR "" → show question, read response into VAR # # At the end, captured values are printed as KEY=VALUE for the agent to parse. -# -# `capture` prints its value back to the terminal, where the agent reads it — so -# capture observations, and leave signing in to the user as a `step`. set -euo pipefail diff --git a/.agents/skills/domain-driven-design/SKILL.md b/.agents/skills/domain-driven-design/SKILL.md new file mode 100644 index 0000000..808baeb --- /dev/null +++ b/.agents/skills/domain-driven-design/SKILL.md @@ -0,0 +1,35 @@ +--- +name: domain-driven-design +description: Model business rules and consistency boundaries using domain-driven design. Use when choosing aggregates or bounded contexts, mapping domain relationships, or correcting a mismatch between business language and code. +license: MIT +metadata: + author: wondelai + version: "1.4.0" +--- + +# Domain-Driven Design + +Model the requested business behavior, not an idealized architecture. Start with the user's scenarios, existing code, root `CONTEXT.md`, and relevant accepted ADRs. + +## Shared constraints + +- Preserve the repository's language and existing architectural decisions. Surface conflicts with evidence rather than silently replacing them. +- Follow [the development data lifecycle](../../../AGENTS.md#development-data-lifecycle): replace obsolete development schemas/contracts and update callers and tests together. Compatibility patterns apply only to genuine external constraints or a later explicit production-data decision. +- Choose aggregate boundaries from invariants and ownership. Preserve [the accepted rental transaction design](../../../docs/adr/0011-rental-aggregate-boundaries.md), including atomic operations across aggregate documents. +- Add events, factories, translation layers, or separate contexts only for a current business or integration need. Simple CRUD is valid; a score or wider architecture audit is optional, not a completion gate. +- Honor the requested deliverable: a design or review does not itself authorize implementation, migrations, or external changes. + +## Use the relevant reference + +Read only the branch needed by the task: + +- Resolving ambiguous terms or maintaining a glossary: [ubiquitous language](references/ubiquitous-language.md). +- Choosing context boundaries or mapping integrations: [bounded contexts](references/bounded-contexts.md). +- Deciding identity, value semantics, aggregate ownership, or atomicity: [building blocks](references/building-blocks.md). +- Designing a real event flow or evaluating event sourcing: [domain events](references/domain-events.md). +- Defining persistence contracts, reconstruction, or complex creation: [repositories and factories](references/repositories-factories.md). +- Prioritizing modeling effort or comparing build/buy choices: [strategic design](references/strategic-design.md). + +## Outcome + +Explain the proposed concepts, invariant owners, important transitions, and how the design handles failure and concurrency. Show only the alternatives that matter, identify unresolved domain questions, and propose checks that would falsify the design. When implementation is requested, update affected callers, tests, and domain documentation together. diff --git a/.agents/skills/domain-driven-design/references/bounded-contexts.md b/.agents/skills/domain-driven-design/references/bounded-contexts.md new file mode 100644 index 0000000..ca4c90c --- /dev/null +++ b/.agents/skills/domain-driven-design/references/bounded-contexts.md @@ -0,0 +1,32 @@ +# Bounded Contexts and Context Mapping + +A bounded context defines where a domain model and its language apply. It is not automatically a microservice, package, or team-sized deployment. + +## Find a real boundary + +Look for differing business meanings, rules, ownership, or change lifecycles. Check concrete scenarios and existing ADRs. A single context may be correct; excessive translation between closely related concepts can signal an unnecessary split. + +For a requested context map, show the relevant contexts, ownership, upstream/downstream relationships, shared contracts, and any translation. Avoid mapping unrelated systems just to complete a catalog. + +## Choose the relationship + +| Pattern | Useful when | Cost to make explicit | +| -------------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------- | +| Shared kernel | Closely collaborating owners genuinely share a small model | Coordinated changes and tests | +| Customer–supplier | The upstream team can accommodate downstream needs | Planning and contract dependency | +| Conformist | Accepting the upstream model is cheaper and safe for this use case | Coupling to upstream semantics | +| Anti-corruption layer | Foreign semantics would distort local business rules | Translation and its tests | +| Open host service / published language | Several consumers need a documented shared contract | Contract ownership and actual external obligations | +| Separate ways | Integration costs more than limited duplication | Separate implementations and knowledge | +| Partnership | Models must evolve together | Ongoing coordination | +| Big ball of mud | Existing entanglement needs to be named during an assessment | A diagnosis, not a target design | + +Translation can live in an existing adapter or mapper; it does not require a facade/adapter/translator class hierarchy. Validate untrusted external data even when adopting the upstream model directly. + +## Changes and external constraints + +Preserve actual external contracts that the task cannot replace. Legacy coexistence patterns apply to a genuinely external system that must remain available, not to this application's disposable development model. + +For internal model changes, follow [the development lifecycle](../../../../AGENTS.md#development-data-lifecycle): replace obsolete schemas/contracts, update callers and tests together, and reseed rather than introducing compatibility paths. + +Explain a proposed split or merge through its effect on language, invariant ownership, coupling, and coordination. Do not reorganize teams, deploy services, or modify contracts merely because a map was requested. diff --git a/.agents/skills/domain-driven-design/references/building-blocks.md b/.agents/skills/domain-driven-design/references/building-blocks.md new file mode 100644 index 0000000..79932f3 --- /dev/null +++ b/.agents/skills/domain-driven-design/references/building-blocks.md @@ -0,0 +1,35 @@ +# Entities, Value Objects, and Aggregates + +Start from identity and business invariants rather than database tables or a target class count. + +## Identity and value + +An entity retains identity through changes; a value object is interchangeable with another value having the same attributes. Choose equality and mutability accordingly. Use existing identifier conventions rather than adding a new ID scheme by default. + +Value objects should enforce their actual invariants and support immutable transformations. Do not infer business rules from examples: negative money may be valid for one concept and invalid for another. + +## Aggregate ownership + +An aggregate owns a cohesive set of state and invariants, with a root controlling its changes. Keep it small enough to load and update for its real use cases; use references by ID for independent aggregates where the project's model calls for them. + +Ask: + +- Which rules must hold after every successful operation? +- Who owns each lifecycle and may change its state? +- Which state must be read and written together? +- What prevents two concurrent commands from both accepting incompatible outcomes? +- Can a partial result or delayed update be valid? + +Do not embed a growing independent lifecycle merely to make all relationships reachable through one object. + +## Atomicity and concurrency + +Aggregate boundaries and transaction boundaries are related, not identical. When an operation must commit or roll back across multiple aggregate documents, use the supported service-layer transaction required by the domain. + +In this repository, [ADR-0011](../../../../docs/adr/0011-rental-aggregate-boundaries.md) explicitly requires Mongo transactions for relevant rental operations. Preserve optimistic version checks, occupancy contention, and uniqueness constraints; replacing them with asynchronous events or a query-only overlap check would weaken the invariant. + +Use eventual consistency only when delayed or partial progress is acceptable, with explicit failure and recovery handling. Keep remote calls and long-running work outside database transactions. + +## Verify the boundary + +Exercise the actual operation, including invalid transitions, stale versions, duplicate commands, and competing callers where relevant. Prove the invariant at the storage/integration boundary when an in-memory test cannot establish it. Preserve distinct existing test coverage when moving behavior behind a new interface. diff --git a/.agents/skills/domain-driven-design/references/domain-events.md b/.agents/skills/domain-driven-design/references/domain-events.md new file mode 100644 index 0000000..b847a3e --- /dev/null +++ b/.agents/skills/domain-driven-design/references/domain-events.md @@ -0,0 +1,29 @@ +# Domain Events + +Use events for a real notification or asynchronous coordination need. Crossing an aggregate boundary alone is not a reason to introduce an event bus. + +## Define the fact and consumers + +Name a domain event as a completed business fact, distinct from a command or pending operation. Identify its actual consumers and the minimum non-sensitive data they need. Avoid serializing the entire aggregate or inventing metadata fields without a purpose. + +Distinguish internal events from externally consumed contracts. Translate when semantics differ; do not introduce a separate layer merely to change the name. + +## Persistence and delivery + +An event must not claim a state change that rolled back. If delivery after commit must be durable, use the project's existing transactional publication mechanism or justify an outbox that records the state change and pending event atomically. + +For retried delivery, make handlers idempotent using an appropriate key, conditional write, or naturally idempotent operation. Coordinate deduplication and the effect atomically where required; recording a key before a failed effect can lose work. + +Define ordering requirements, retry limits, and observable failure handling. Test duplicate, delayed, out-of-order, and failing delivery where those modes affect the invariant. Do not claim exactly-once behavior merely because duplicate messages are filtered. + +## Do not weaken atomic business operations + +Preserve the cross-aggregate transactions accepted in [ADR-0011](../../../../docs/adr/0011-rental-aggregate-boundaries.md). Asynchronous coordination is suitable only when partial progress and delay are valid. A saga or process manager is justified only by an actual long-running workflow, with explicit recovery semantics. + +## Event sourcing is a separate choice + +Event sourcing makes stored events the source of truth; publishing an event does not require it. Consider it only for a demonstrated need for historical reconstruction or temporal queries that the existing model cannot reasonably serve. + +If chosen, define replay behavior without repeating external side effects, concurrency protection, and the actual retention obligations. Snapshots are an optimization, not initial scaffolding. + +Follow [the development lifecycle](../../../../AGENTS.md#development-data-lifecycle): replace and reseed disposable development events. Retained-history upcasters or compatibility versions require an explicit later production-data decision; genuine external event contracts remain external constraints. diff --git a/.agents/skills/domain-driven-design/references/repositories-factories.md b/.agents/skills/domain-driven-design/references/repositories-factories.md new file mode 100644 index 0000000..dcfb3a4 --- /dev/null +++ b/.agents/skills/domain-driven-design/references/repositories-factories.md @@ -0,0 +1,25 @@ +# Repositories and Factories + +Use this branch when the task concerns persistence contracts or complex object creation, not as a checklist of layers to add. + +## Repository contracts + +Keep domain decisions separate from storage mechanics. Define methods in terms of actual domain operations and queries, following the repository's existing architecture. Avoid generic query escape hatches that leak SQL or document details into domain behavior. + +Aggregate-loading methods return enough valid state for the operation to enforce its invariants. Query-only methods may return explicit read projections; this does not require a new CQRS subsystem. Fetch only needed data and test relevant storage, transaction, and concurrency behavior. + +Follow the persistence technology's actual save/change-tracking semantics. Make absence, failed writes, and stale versions explicit in the contract. + +## Reconstruction versus creation + +Creating a new aggregate validates command inputs and invariants and may record creation events. Loading an existing aggregate preserves its identity and must not replay creation events or other external effects. + +Validate the persisted shape and invariants needed for safe use rather than assuming stored data is trustworthy. Report corrupt or incompatible records; apply the project's disposable-data policy instead of adding legacy readers. + +## Factories and query specifications + +Use a constructor or existing mapper for straightforward creation. A factory earns its place when assembly or selection logic is sufficiently complex, duplicated, or independently testable; it must not produce an invalid aggregate. + +Use query specifications only when actual reusable composition warrants them. A focused repository method is often enough. + +Keep dependencies aligned with existing domain/application/infrastructure ownership. An in-memory test adapter can isolate domain logic, but does not replace integration checks for database behavior. diff --git a/.agents/skills/domain-driven-design/references/strategic-design.md b/.agents/skills/domain-driven-design/references/strategic-design.md new file mode 100644 index 0000000..feaf4c0 --- /dev/null +++ b/.agents/skills/domain-driven-design/references/strategic-design.md @@ -0,0 +1,23 @@ +# Strategic Design + +Use this branch when the user asks where to invest modeling effort or whether to build, reuse, or buy a capability. + +## Classify by the actual business + +- **Core:** the behavior that provides the product's distinctive value. +- **Supporting:** business-specific functionality needed to deliver that value. +- **Generic:** a broadly shared problem where existing solutions may fit. + +These are decision aids, not fixed quality tiers or staffing rules. Security, accessibility, and data integrity remain required in every category. A domain's classification can change as the product changes. + +## Compare concrete options + +Start with the existing implementation and stack. Compare options against needed behavior, integration cost, operational burden, lock-in, privacy/security constraints, and available expertise. + +Do not prescribe a vendor, dependency, outsourcing arrangement, or percentage allocation from a generic example. Verify current primary sources before making a real product recommendation and preserve the user's selected product unless changing it is in scope. + +## Distill the decision + +Identify the concepts and invariants that deserve the most attention, the surrounding functionality that can stay simple, and the evidence supporting that choice. Update existing domain documents only when requested; propose an ADR for a consequential, non-obvious trade-off rather than creating a parallel architecture handbook. + +End with a focused recommendation and its limitations. A strategy discussion does not authorize purchasing services, provisioning infrastructure, or reorganizing the codebase. diff --git a/.agents/skills/domain-driven-design/references/ubiquitous-language.md b/.agents/skills/domain-driven-design/references/ubiquitous-language.md new file mode 100644 index 0000000..6d51a2f --- /dev/null +++ b/.agents/skills/domain-driven-design/references/ubiquitous-language.md @@ -0,0 +1,22 @@ +# Ubiquitous Language + +Use this branch to resolve a terminology problem, not to rename every technical class. Read [the configured domain layout](../../../../docs/agents/domain.md) and the existing glossary before proposing terms. + +## Resolve the meaning + +Take a concrete business scenario and identify its actors, nouns, actions, and state transitions. Compare the user's explanation with the code and accepted decisions. + +- One term with different meanings: identify the actual distinction and its context. +- Several terms for the same concept: propose one canonical term and record relevant aliases. +- A lifecycle difference: decide whether it represents a state of one identity or a distinct concept. +- An unfamiliar name: check domain usage before assuming the model is wrong. + +Ask for domain clarification only where the evidence does not settle a consequential choice. Do not invent an expert's agreement. + +## Record and apply + +Update root `CONTEXT.md` in its existing format when glossary maintenance is requested. Keep definitions concise, show important relationships, and distinguish agreed language from proposals. Technical names such as service or DTO may remain appropriate outside the domain glossary. + +A terminology discussion does not automatically authorize a code-wide rename. For an authorized rename, find all affected code, tests, schemas, contracts, and documents; replace them together under the development lifecycle policy. Do not introduce migration or API-version compatibility for disposable development data. + +Different contexts may legitimately use different meanings. Do not split a coherent context merely because a word is ambiguous; first determine whether the underlying rules or ownership actually differ. diff --git a/.agents/skills/edit-article/SKILL.md b/.agents/skills/edit-article/SKILL.md new file mode 100644 index 0000000..959bfd9 --- /dev/null +++ b/.agents/skills/edit-article/SKILL.md @@ -0,0 +1,14 @@ +--- +name: edit-article +description: Edit and improve articles by restructuring sections, improving clarity, and tightening prose. Use when user wants to edit, revise, or improve an article draft. +--- + +1. First, divide the article into sections based on its headings. Think about the main points you want to make during those sections. + +Consider that information is a directed acyclic graph, and that pieces of information can depend on other pieces of information. Make sure that the order of the sections and their contents respects these dependencies. + +Preserve the requested structure and voice. Confirm restructuring only if it would materially change the requested edit or an explicitly interactive workflow calls for it. + +2. For each section: + +2a. Rewrite the section to improve clarity, coherence, and flow. Let paragraph length follow the content, audience, and requested format; do not impose an arbitrary character cap. diff --git a/.agents/skills/edit-article/agents/openai.yaml b/.agents/skills/edit-article/agents/openai.yaml new file mode 100644 index 0000000..aa6dd40 --- /dev/null +++ b/.agents/skills/edit-article/agents/openai.yaml @@ -0,0 +1,5 @@ +interface: + display_name: "Edit Article" + short_description: "Restructure and tighten a draft" +policy: + allow_implicit_invocation: false diff --git a/.agents/skills/git-guardrails-claude-code/SKILL.md b/.agents/skills/git-guardrails-claude-code/SKILL.md new file mode 100644 index 0000000..b76bf92 --- /dev/null +++ b/.agents/skills/git-guardrails-claude-code/SKILL.md @@ -0,0 +1,103 @@ +--- +name: git-guardrails-claude-code +description: Set up Claude Code hooks to block dangerous git commands (push, reset --hard, clean, branch -D, etc.) before they execute. Use when user wants to prevent destructive git operations, add git safety hooks, or block git push/reset in Claude Code. +--- + +# Setup Git Guardrails + +Sets up a PreToolUse hook that inspects direct Git commands before Claude executes them. It requires Bash and Python 3; missing Python or invalid input blocks the command with exit code 2. + +## What Gets Blocked + +- Direct `git push` commands, including force flags, ordinary global options, and shell command separators +- `git reset --hard` +- `git clean -f` / `git clean -fd` +- `git branch -D` +- `git checkout .` / `git restore .` + +When blocked, Claude sees a message telling it that it does not have authority to access these commands. + +This is a conservative command guard, not a shell sandbox. It does not evaluate aliases, shell expansion, or commands hidden in scripts. Unsupported Git global options require manual inspection, and literal Git tokens in arguments can also be blocked. Keep execution permissions enforced by the host. + +## Steps + +### 1. Ask scope + +Ask the user: install for **this project only** (`.claude/settings.json`) or **all projects** (`~/.claude/settings.json`)? + +### 2. Copy the hook script + +The bundled script is at: [scripts/block-dangerous-git.sh](scripts/block-dangerous-git.sh) + +Copy it to the target location based on scope: + +- **Project**: `.claude/hooks/block-dangerous-git.sh` +- **Global**: `~/.claude/hooks/block-dangerous-git.sh` + +Make it executable with `chmod +x`. + +### 3. Add hook to settings + +Add to the appropriate settings file: + +**Project** (`.claude/settings.json`): + +```json +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous-git.sh" + } + ] + } + ] + } +} +``` + +**Global** (`~/.claude/settings.json`): + +```json +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "~/.claude/hooks/block-dangerous-git.sh" + } + ] + } + ] + } +} +``` + +If the settings file already exists, merge the hook into existing `hooks.PreToolUse` array — don't overwrite other settings. + +### 4. Ask about customization + +Ask if user wants to add or remove any patterns from the blocked list. Edit the copied script accordingly. + +### 5. Verify + +Run the bundled checks; every command is an inert input string: + +```bash +python3 scripts/test_guardrail.py +``` + +Then test the installed copy: + +```bash +echo '{"tool_input":{"command":"git push origin main"}}' | +``` + +Should exit with code 2 and print a BLOCKED message to stderr. diff --git a/.agents/skills/git-guardrails-claude-code/agents/openai.yaml b/.agents/skills/git-guardrails-claude-code/agents/openai.yaml new file mode 100644 index 0000000..3f5d756 --- /dev/null +++ b/.agents/skills/git-guardrails-claude-code/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Git Guardrails for Claude Code" + short_description: "Block dangerous git commands" diff --git a/.agents/skills/git-guardrails-claude-code/scripts/block-dangerous-git.sh b/.agents/skills/git-guardrails-claude-code/scripts/block-dangerous-git.sh new file mode 100755 index 0000000..2d77354 --- /dev/null +++ b/.agents/skills/git-guardrails-claude-code/scripts/block-dangerous-git.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +if ! command -v python3 >/dev/null 2>&1; then + printf '%s\n' 'BLOCKED: Python 3 is required to inspect this command.' >&2 + exit 2 +fi + +exec python3 -c ' +import itertools +import json +import shlex +import sys + +def block(reason): + print("BLOCKED: " + reason, file=sys.stderr) + sys.exit(2) + +try: + command = json.load(sys.stdin)["tool_input"]["command"] + if not isinstance(command, str) or not command.strip(): + raise ValueError("missing command") + lexer = shlex.shlex(command.replace("\\\n", ""), posix=True, punctuation_chars=";&|()\n") + lexer.whitespace = " \t\r" + lexer.whitespace_split = True + tokens = list(lexer) +except (ValueError, KeyError, TypeError): + block("Cannot parse the command input.") + +value_options = {"-C", "-c", "--git-dir", "--work-tree", "--namespace", "--config-env"} +flag_options = {"-p", "--paginate", "-P", "--no-pager", "--bare", "--literal-pathspecs"} +# ponytail: conservative token inspection; shell expansion and aliases require sandbox enforcement. +for separator, group in itertools.groupby(tokens, lambda token: bool(token) and token[0] in ";&|()\n"): + if separator: + continue + words = list(group) + for index, word in enumerate(words): + if word.rsplit("/", 1)[-1] != "git": + continue + args = words[index + 1:] + while args and args[0].startswith("-"): + option = args.pop(0) + if option in value_options: + if not args: + block("Missing Git option value.") + args.pop(0) + elif option in flag_options or option.split("=", 1)[0] in value_options: + continue + else: + block("Unsupported Git option; inspect this command manually.") + if not args: + continue + operation, *args = args + short_flags = "".join(arg[1:] for arg in args if arg.startswith("-") and not arg.startswith("--")) + force = "--force" in args or "f" in short_flags + dangerous = ( + operation == "push" + or operation == "reset" and "--hard" in args + or operation == "clean" and force + or operation == "branch" and ("D" in short_flags or force and ("d" in short_flags or "--delete" in args)) + or operation in {"checkout", "restore"} and "." in args + ) + if dangerous: + block("Git " + operation + " is disabled by this hook.") +' diff --git a/.agents/skills/git-guardrails-claude-code/scripts/test_guardrail.py b/.agents/skills/git-guardrails-claude-code/scripts/test_guardrail.py new file mode 100644 index 0000000..3f5ea85 --- /dev/null +++ b/.agents/skills/git-guardrails-claude-code/scripts/test_guardrail.py @@ -0,0 +1,61 @@ +"""Run with python3; command payloads are inspected, never executed.""" + +import json +import os +from pathlib import Path +import subprocess + + +hook = Path(__file__).with_name("block-dangerous-git.sh") + + +def check(payload, expected, env=None): + result = subprocess.run( + ["/bin/bash", str(hook)], input=payload, text=True, capture_output=True, env=env + ) + assert result.returncode == expected, (payload, result.returncode, result.stderr) + if expected == 2: + assert result.stderr.startswith("BLOCKED:"), result.stderr + + +for command in ( + "git push origin main", + "git push --force-with-lease origin main", + "git -C /tmp/example push origin main", + "git -c core.pager=cat --no-pager push", + "git --git-dir=/tmp/example push", + "git push", + "git status;git push", + "git status\ngit push", + "git \\\npush origin main", + "git pu\\\nsh origin main", + "git reset --ha\\\nrd HEAD", + "git reset --hard HEAD", + "git clean -xdf", + "git clean --force", + "git branch -D topic", + "git branch -df topic", + "git branch -vD topic", + "git branch --delete --force topic", + "git checkout -- .", + "git restore .", +): + check(json.dumps({"tool_input": {"command": command}}), 2) + +for command in ( + "git status", + "git diff", + "git -C /tmp/example status", + "git branch -d merged-topic", + "git reset --soft HEAD~1", + "echo 'git push origin main'", + 'echo ""', + "git reset --soft HEAD; echo --hard", +): + check(json.dumps({"tool_input": {"command": command}}), 0) + +for payload in ("", "{", "{}", "[]", '{"tool_input":{"command":null}}'): + check(payload, 2) +check('{"tool_input":{"command":"git status"}}', 2, {**os.environ, "PATH": ""}) +check('{"tool_input":{"command":"git \\\""}}', 2) +print("Guardrail checks passed") diff --git a/.agents/skills/grill-me/SKILL.md b/.agents/skills/grill-me/SKILL.md index 9470cfc..b69573b 100644 --- a/.agents/skills/grill-me/SKILL.md +++ b/.agents/skills/grill-me/SKILL.md @@ -1,7 +1,6 @@ --- name: grill-me description: A relentless interview to sharpen a plan or design. -disable-model-invocation: true --- Run a `/grilling` session. diff --git a/.agents/skills/grill-with-docs/SKILL.md b/.agents/skills/grill-with-docs/SKILL.md index bed05d2..fa1bf7d 100644 --- a/.agents/skills/grill-with-docs/SKILL.md +++ b/.agents/skills/grill-with-docs/SKILL.md @@ -1,7 +1,6 @@ --- name: grill-with-docs description: A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go. -disable-model-invocation: true --- Run a `/grilling` session, using the `/domain-modeling` skill. diff --git a/.agents/skills/grilling/SKILL.md b/.agents/skills/grilling/SKILL.md index 95bd01e..52d8eb3 100644 --- a/.agents/skills/grilling/SKILL.md +++ b/.agents/skills/grilling/SKILL.md @@ -3,20 +3,10 @@ name: grilling description: Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases. --- -Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: every decision branches into the decisions that hang off it. +Interview me relentlessly about every aspect of this until we reach a shared understanding. Walk down each branch of the decision tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer. -Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled — the questions you can ask _now_ without guessing at answers you haven't heard yet. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait for the user's answers before the next round. +Ask the questions one at a time, waiting for feedback on each question before continuing. Asking multiple questions at once is bewildering. -Each question should be formatted like so: +If a *fact* can be found by exploring the environment (filesystem, tools, etc.), look it up rather than asking me. The *decisions*, though, are mine — put each one to me and wait for my answer. -``` -❓ **Q1** - ****: - -➡️ -``` - -Each round the user answers reshapes the tree — settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one. - -Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it — don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report — ask the rest of the frontier now. The _decisions_ are the user's — put each to them and wait. - -The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding. +Do not act on it until I confirm we have reached a shared understanding. diff --git a/.agents/skills/grilling/agents/openai.yaml b/.agents/skills/grilling/agents/openai.yaml index ddbdb96..85b1260 100644 --- a/.agents/skills/grilling/agents/openai.yaml +++ b/.agents/skills/grilling/agents/openai.yaml @@ -1,3 +1,3 @@ interface: display_name: "Grilling" - short_description: "Stress-test thinking a round of questions at a time" + short_description: "Stress-test thinking one question at a time" diff --git a/.agents/skills/handoff/SKILL.md b/.agents/skills/handoff/SKILL.md index 043d9e1..efdb9a7 100644 --- a/.agents/skills/handoff/SKILL.md +++ b/.agents/skills/handoff/SKILL.md @@ -1,11 +1,9 @@ --- name: handoff description: Compact the current conversation into a handoff document for another agent to pick up. -argument-hint: "What will the next session be used for?" -disable-model-invocation: true --- -Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS - not the current workspace. +Write a handoff document summarising the current conversation so a fresh agent can continue the work. Honor a user-selected output path; otherwise save to the operating system's temporary directory rather than the workspace. Include a "suggested skills" section in the document, which suggests skills that the agent should invoke. diff --git a/.agents/skills/implement/SKILL.md b/.agents/skills/implement/SKILL.md index 7a0b11f..5552607 100644 --- a/.agents/skills/implement/SKILL.md +++ b/.agents/skills/implement/SKILL.md @@ -1,15 +1,16 @@ --- name: implement description: "Implement a piece of work based on a spec or set of tickets." -disable-model-invocation: true --- Implement the work described by the user in the spec or tickets. -Use /tdd where possible, at pre-agreed seams. +Before editing, record the starting commit, the original spec/acceptance criteria, and existing worktree changes. Keep a task-owned path/patch list so verification can separate your work from unrelated changes. + +Use /tdd when test-first work is requested or useful. Reuse established test seams and infer routine choices from the existing tests; ask only about consequential unresolved design decisions. Run typechecking regularly, single test files regularly, and the full test suite once at the end. -Once done, use /code-review to review the work. +Once done, use /code-review with the original spec, recorded baseline, and task-owned paths/patch. Request the current working result, including staged, unstaged, and new files, rather than a HEAD-only review. -Commit your work to the current branch. +Commit task-owned changes only when committing is part of the requested workflow; otherwise hand off the verified diff. Preserve unrelated staged and unstaged work. A request to commit already supplies that authority; do not ask again or infer permission to push. diff --git a/.agents/skills/improve-codebase-architecture/HTML-REPORT.md b/.agents/skills/improve-codebase-architecture/HTML-REPORT.md index 17f6d2c..83fd8a1 100644 --- a/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +++ b/.agents/skills/improve-codebase-architecture/HTML-REPORT.md @@ -1,123 +1,56 @@ -# HTML Report Format +# HTML Architecture Report -The architectural review is rendered as a single self-contained HTML file in the OS temp directory. Tailwind and Mermaid both come from CDNs. Mermaid handles graph-shaped diagrams reliably; hand-built divs and inline SVG handle the more editorial visuals (mass diagrams, cross-sections). Mix the two — don't lean on Mermaid for everything, it'll start to look generic. +Use only when HTML output is requested or a visual artifact materially helps. Honor the selected location; otherwise write to a fresh temporary file. Keep it usable offline with semantic HTML, a small stylesheet, and static SVG when useful. -## Scaffold +## Minimal scaffold ```html - Architecture review — {{repo name}} - - + + Architecture review - -
-
...
-
...
-
...
+ +
+

Architecture review

+
``` -## Header +## Findings -Repo name, date, and a compact legend: solid box = module, dashed line = seam, red arrow = leakage, thick dark box = deep module. No introduction paragraph — straight into the candidates. +For each candidate, include its location, observed problem, proposed change, concrete benefit, and recommendation strength. Identify ADR conflicts and uncertainty. Omit unsupported or merely speculative candidates rather than filling a quota. -## Candidate card +Use before/after visuals for relationships that prose does not explain clearly. A small dependency graph, sequence, or nested module view may help; a plain paragraph often suffices. Give SVGs accessible titles and descriptions, and do not communicate meaning through color alone. -The diagrams carry the weight. Prose is sparse, plain, and uses the glossary terms (from the `/codebase-design` skill) without ceremony. +Use an existing renderer only when it adds value; embed static output rather than requiring external scripts at viewing time. Escape repository text inserted into HTML. Do not execute code from issues or findings to build the report. -Each candidate is one `
`: - -- **Title** — short, names the deepening (e.g. "Collapse the Order intake pipeline"). -- **Badge row** — recommendation strength (`Strong` = emerald, `Worth exploring` = amber, `Speculative` = slate), plus a tag for the dependency category (`in-process`, `local-substitutable`, `ports & adapters`, `mock`). -- **Files** — monospaced list, `font-mono text-sm`. -- **Before / After diagram** — the centrepiece. Two columns, side by side. See patterns below. -- **Problem** — one sentence. What hurts. -- **Solution** — one sentence. What changes. -- **Wins** — bullets, ≤6 words each. e.g. "Tests hit one interface", "Pricing logic stops leaking", "Delete 4 shallow wrappers". -- **ADR callout** (if applicable) — one line in an amber-tinted box. - -No paragraphs of explanation. If the diagram needs a paragraph to be understood, redraw the diagram. - -## Diagram patterns - -Pick the pattern that fits the candidate. Mix them. Don't make every diagram look the same — variety is part of the point. - -### Mermaid graph (the workhorse for dependencies / call flow) - -Use a Mermaid `flowchart` or `graph` when the point is "X calls Y calls Z, and look at the mess." Wrap it in a Tailwind-styled card so it doesn't feel parachuted in. Style with classDef to colour leakage edges red and the deep module dark. Sequence diagrams work well for "before: 6 round-trips; after: 1." - -```html -
-
-    flowchart LR
-      A[OrderHandler] --> B[OrderValidator]
-      B --> C[OrderRepo]
-      C -.leak.-> D[PricingClient]
-      classDef leak stroke:#dc2626,stroke-width:2px;
-      class C,D leak
-  
-
-``` - -### Hand-built boxes-and-arrows (when Mermaid's layout fights you) - -Modules as `
`s with borders and labels. Arrows as inline SVG `` or `` elements positioned absolutely over a relative container. Reach for this when you want the "after" diagram to feel like one thick-bordered deep module with greyed-out internals — Mermaid won't render that with the right weight. - -### Cross-section (good for layered shallowness) - -Stack horizontal bands (`h-12 border-l-4`) to show layers a call passes through. Before: 6 thin layers each doing nothing. After: 1 thick band labelled with the consolidated responsibility. - -### Mass diagram (good for "interface as wide as implementation") - -Two rectangles per module — one for interface surface area, one for implementation. Before: interface rectangle is nearly as tall as the implementation rectangle (shallow). After: interface rectangle is short, implementation rectangle is tall (deep). - -### Call-graph collapse - -Before: a tree of function calls rendered as nested boxes. After: the same tree collapsed into one box, with the now-internal calls shown faded inside it. - -## Style guidance - -- Lean editorial, not corporate-dashboard. Generous whitespace. Serif optional for headings (`font-serif` works well with stone/slate). -- Colour sparingly: one accent (emerald or indigo) plus red for leakage and amber for warnings. -- Keep diagrams ~320px tall so before/after sits comfortably side by side without scrolling. -- Use `text-xs uppercase tracking-wider` for module labels inside diagrams — they should read as schematic, not as UI. -- The only scripts are the Tailwind CDN and the Mermaid ESM import. The report is otherwise static — no app code, no interactivity beyond Mermaid's own rendering. - -## Top recommendation section - -One larger card. Candidate name, one sentence on why, anchor link to its card. That's it. +End with the recommended next step and its reason. The report does not authorize implementation. ## Tone -Plain English, concise — but the architectural nouns and verbs come straight from the `/codebase-design` skill. Concision is not an excuse to drift. +Use concise, evidence-backed explanations. Preserve project vocabulary and describe the relevant design concept where useful. -**Use exactly:** module, interface, implementation, depth, deep, shallow, seam, adapter, leverage, locality. +Terms such as module, interface, depth, seam, leverage, and locality can clarify the design. -**Never substitute:** component, service, unit (for module) · API, signature (for interface) · boundary (for seam) · layer, wrapper (for module, when you mean module). +Names such as component, service, API, boundary, layer, or wrapper are appropriate when they accurately name the project element. Do not replace established terms just to satisfy a glossary. **Phrasings that fit the style:** - "Order intake module is shallow — interface nearly matches the implementation." - "Pricing leaks across the seam." - "Deepen: one interface, one place to test." -- "Two adapters justify the seam: HTTP in prod, in-memory in tests." +- "The payment boundary isolates network failures in tests." -**Wins bullets** name the gain in glossary terms: *"locality: bugs concentrate in one module"*, *"leverage: one interface, N call sites"*, *"interface shrinks; implementation absorbs the wrappers"*. Don't write *"easier to maintain"* or *"cleaner code"* — those terms aren't in the glossary and don't earn their place. +Describe concrete gains such as fewer caller obligations or preserved coverage through one contract. Avoid unsupported claims of cleaner code, regardless of vocabulary. -No hedging, no throat-clearing, no "it's worth noting that…". If a sentence could be a bullet, make it a bullet. If a bullet could be cut, cut it. If a term isn't in the `/codebase-design` glossary, reach for one that is before inventing a new one. +State uncertainty and trade-offs honestly. Use only the formatting needed to make the finding understandable. diff --git a/.agents/skills/improve-codebase-architecture/SKILL.md b/.agents/skills/improve-codebase-architecture/SKILL.md index 529761a..962758a 100644 --- a/.agents/skills/improve-codebase-architecture/SKILL.md +++ b/.agents/skills/improve-codebase-architecture/SKILL.md @@ -1,7 +1,6 @@ --- name: improve-codebase-architecture -description: Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick. -disable-model-invocation: true +description: Review a codebase for module-deepening opportunities and explain the most useful candidates. Use for an architecture survey or a specified area of architectural friction. --- # Improve Codebase Architecture @@ -10,7 +9,7 @@ Surface architectural friction and propose **deepening opportunities** — refac This command is _informed_ by the project's domain model and built on a shared design vocabulary: -- Run the `/codebase-design` skill for the architecture vocabulary (**module**, **interface**, **depth**, **seam**, **adapter**, **leverage**, **locality**) and its principles (the deletion test, "the interface is the test surface", "one adapter = hypothetical seam, two = real"). Use these terms exactly in every suggestion — don't drift into "component," "service," "API," or "boundary." +- Use `/codebase-design` to reason about module depth, caller contracts, locality, and justified test seams. Preserve project names such as component, service, or API; explain concepts without imposing a competing vocabulary or adapter count. - The domain language in `CONTEXT.md` gives names to good seams; ADRs in `docs/adr/` record decisions this command should not re-litigate. ## Process @@ -24,7 +23,7 @@ This command is _informed_ by the project's domain model and built on a shared d Read the project's domain glossary (`CONTEXT.md`) and any ADRs in the area you're touching first. -Then spawn a sub-agent to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction: +Explore directly or delegate bounded independent areas through the available tools when useful. Note concrete friction: - Where does understanding one concept require bouncing between many small modules? - Where are modules **shallow** — interface nearly as complex as the implementation? @@ -34,11 +33,11 @@ Then spawn a sub-agent to walk the codebase. Don't follow rigid heuristics — e Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want. -### 2. Present candidates as an HTML report +### 2. Present the useful candidates -Write a self-contained HTML file to the OS temp directory so nothing lands in the repo. Resolve the temp dir from `$TMPDIR`, falling back to `/tmp` (or `%TEMP%` on Windows), and write to `/architecture-review-.html` so each run gets a fresh file. Open it for the user — `xdg-open ` on Linux, `open ` on macOS, `start ` on Windows — and tell them the absolute path. +Honor the requested format and output path. A concise Markdown report is enough for a few findings. For a requested visual HTML report, use a fresh temporary path by default and provide its location; open it only when requested. -The report uses **Tailwind via CDN** for layout and styling, and **Mermaid via CDN** for diagrams where a graph/flow/sequence reliably communicates the structure. Mix Mermaid with hand-crafted CSS/SVG visuals — use Mermaid when relationships are graph-shaped (call graphs, dependencies, sequences), and hand-built divs/SVG when you want something more editorial (mass diagrams, cross-sections, collapse animations). Each candidate gets a **before/after visualisation**. Be visual. +Use a diagram only when relationships or a before/after change are clearer visually. Prefer static HTML/CSS/SVG or existing renderers; do not add CDN scripts or dependencies for a simple report. For each candidate, render a card with: @@ -46,24 +45,24 @@ For each candidate, render a card with: - **Problem** — why the current architecture is causing friction - **Solution** — plain English description of what would change - **Benefits** — explained in terms of locality and leverage, and how tests would improve -- **Before / After diagram** — side-by-side, custom-drawn, illustrating the shallowness and the deepening +- **Before / After diagram** — only when it materially clarifies the change - **Recommendation strength** — one of `Strong`, `Worth exploring`, `Speculative`, rendered as a badge End the report with a **Top recommendation** section: which candidate you'd tackle first and why. -**Use CONTEXT.md vocabulary for the domain, and the `/codebase-design` vocabulary for the architecture.** If `CONTEXT.md` defines "Order," talk about "the Order intake module" — not "the FooBarHandler," and not "the Order service." +Use `CONTEXT.md` vocabulary for the domain and accurate project names for the implementation. Design terms should clarify the proposed change, not rename existing concepts without need. **ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly in the card (e.g. a warning callout: _"contradicts ADR-0007 — but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids. -See [HTML-REPORT.md](HTML-REPORT.md) for the full HTML scaffold, diagram patterns, and styling guidance. +For HTML output, read [HTML-REPORT.md](HTML-REPORT.md) for a small offline scaffold and relevant diagram options. -Do NOT propose interfaces yet. After the file is written, ask the user: "Which of these would you like to explore?" +Return the requested survey with a recommendation. If the user wants an interactive design session, offer to explore a candidate; otherwise stop at the report. ### 3. Grilling loop -Once the user picks a candidate, run the `/grilling` skill to walk the decision tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive. +When the user requests an interview about a candidate, use `/grilling` for unresolved constraints and decisions. Reuse existing answers and honor a request for a direct design recommendation instead. -Side effects happen inline as decisions crystallize — run the `/domain-modeling` skill to keep the domain model current as you go: +When domain-document updates are within scope, use `/domain-modeling` as decisions crystallize; a read-only architecture report should only recommend these updates: - **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md`. Create the file lazily if it doesn't exist. - **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there. diff --git a/.agents/skills/loop-me/SKILL.md b/.agents/skills/loop-me/SKILL.md new file mode 100644 index 0000000..3dc6261 --- /dev/null +++ b/.agents/skills/loop-me/SKILL.md @@ -0,0 +1,30 @@ +--- +name: loop-me +description: Grill me about specs for the workflows I want to build, within this workspace. +--- + +Run a stateful `/grilling` session whose output is **workflow** specs. Use one question at a time with a recommendation, focused on the requested workflow. Update relevant specs as decisions resolve; preserve unrelated specs and remove an existing one only when the user has authorized that scope change. + +## The loop lens + +A **loop** is a recurring pattern in the user's life: their career, their week, their morning, a single repeated activity. Picturing a life as loops within loops reveals how predictable its activities really are — which is what makes them worth **delegating**. Use the lens to find loops worth specifying, and propose ones the user hasn't noticed. + +A **workflow** is the spec of one loop, made real. You run a workflow on a loop — the loop is its running instantiation. Workflows live in `workflows/*.md` and are the source of truth. + +## Vocabulary + +A shared language, reached for only when a workflow calls for it — never a checklist. **Mandate nothing structural**: a workflow needs no AI, no checkpoint, and no schedule unless the grilling shows it does. + +- **Trigger** — what fires each run: an **event** (a new email, a new issue) or a **schedule** (every morning). Event-triggering is usually the more efficient. +- **Checkpoint** — a human-in-the-loop point where the user is asked to verify or decide. Some workflows have none and run autonomously; some use no AI at all. +- **Push right** — defer the checkpoint as far as it will go. Do maximal work before involving the human, so they are asked once, late, with everything prepared. +- **Brief** — what a checkpoint presents: a tight, decision-ready summary — what was produced, why, and a link down to the asset itself — never the raw output. The user reads a brief, not a draft. Speed of review is imperative. + +## Definition of done + +A workflow spec is done when its scope, inputs, outputs, authority, failure handling, and acceptance criteria are actionable. Record safe assumptions and optional future questions; keep interviewing only about decisions that materially affect the requested workflow. + +## The workspace + +- `workflows/*.md` — one spec per workflow. +- `NOTES.md` — relevant tools, channels, preferences, and terminology already learned. Missing notes do not block a clear request; ask only for context needed by this workflow and avoid collecting unrelated personal details. diff --git a/.agents/skills/loop-me/agents/openai.yaml b/.agents/skills/loop-me/agents/openai.yaml new file mode 100644 index 0000000..1a4f411 --- /dev/null +++ b/.agents/skills/loop-me/agents/openai.yaml @@ -0,0 +1,5 @@ +interface: + display_name: "Loop Me" + short_description: "Spec the workflows you want to build" +policy: + allow_implicit_invocation: false diff --git a/.agents/skills/luna-verified-implement/SKILL.md b/.agents/skills/luna-verified-implement/SKILL.md new file mode 100644 index 0000000..fc76659 --- /dev/null +++ b/.agents/skills/luna-verified-implement/SKILL.md @@ -0,0 +1,24 @@ +--- +name: luna-verified-implement +description: Implement a spec or ticket with gpt-6-luna implementation subagents and independently verify the final change with the orchestrator's model and reasoning effort. Use when the user asks to implement, build, or fix work and wants delegated implementation with a verification pass. +--- + +# Luna Verified Implement + +Invoke `$implement` and follow it as the baseline workflow. Treat this skill as its delegated, verified variant. + +## Workflow + +1. Read the spec or ticket, repository guidance, affected flow, and existing worktree changes. Record the starting commit and task-owned scope; preserve unrelated changes. +2. Split only genuinely independent, bounded work. If the change is small or one seam owns it, use one implementation agent; do not manufacture parallelism. +3. Spawn each implementation agent with `fork_turns: "none"`, `model: "gpt-6-luna"`, and `reasoning_effort: "max"`. Give each its exact owned files/seam, acceptance criteria, required checks, and task-local context. Tell agents not to overwrite or revert other agents' work. +4. Wait for agents, inspect their diffs, resolve overlaps minimally, and run the implementation checks required by `$implement`: targeted tests and typecheck during work, then the full relevant suite once. +5. Spawn a fresh, read-only verification agent with `fork_turns: "none"`, `model` and `reasoning_effort` set to the orchestrator's current values. Do not hard-code a different model or reasoning effort for verification. Give it the original request, current diff, and test output, but not implementation conclusions. Ask it to check correctness, regressions, specification coverage, and test evidence. Have it report findings without changing files. +6. Fix confirmed findings, rerun affected checks, and repeat verification once if fixes were needed. Run `$code-review` on the current working result (including staged, unstaged, and new files), passing the original spec, starting commit, and task-owned scope. Follow `$implement`'s commit boundary: commit only when requested, otherwise hand off the verified diff. + +## Delegation rules + +- Keep all agents in the same workspace; assign non-overlapping ownership before they edit. +- Use at most the available concurrency slots. Reserve one slot for coordination and verification. +- Do not delegate secrets, production writes, destructive operations, or changes requiring new user authority. +- If subagents are unavailable, implement directly, run the same checks, and state that delegated verification was unavailable. diff --git a/.agents/skills/luna-verified-implement/agents/openai.yaml b/.agents/skills/luna-verified-implement/agents/openai.yaml new file mode 100644 index 0000000..d17ca17 --- /dev/null +++ b/.agents/skills/luna-verified-implement/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Luna Verified Implement" + short_description: "Delegate implementation and verify it" + default_prompt: "Use $luna-verified-implement to implement this task with Luna agents and verify the result." diff --git a/.agents/skills/migrate-to-shoehorn/SKILL.md b/.agents/skills/migrate-to-shoehorn/SKILL.md new file mode 100644 index 0000000..ae4f965 --- /dev/null +++ b/.agents/skills/migrate-to-shoehorn/SKILL.md @@ -0,0 +1,118 @@ +--- +name: migrate-to-shoehorn +description: Migrate test files from `as` type assertions to @total-typescript/shoehorn. Use when user mentions shoehorn, wants to replace `as` in tests, or needs partial test data. +--- + +# Migrate to Shoehorn + +## Why shoehorn? + +`shoehorn` lets you pass partial data in tests while keeping TypeScript happy. It replaces `as` assertions with type-safe alternatives. + +**Test code only.** Never use shoehorn in production code. + +Problems with `as` in tests: + +- Trained not to use it +- Must manually specify target type +- Double-as (`as unknown as Type`) for intentionally wrong data + +## Install + +```bash +npm i @total-typescript/shoehorn +``` + +## Migration patterns + +### Large objects with few needed properties + +Before: + +```ts +type Request = { + body: { id: string }; + headers: Record; + cookies: Record; + // ...20 more properties +}; + +it("gets user by id", () => { + // Only care about body.id but must fake entire Request + getUser({ + body: { id: "123" }, + headers: {}, + cookies: {}, + // ...fake all 20 properties + }); +}); +``` + +After: + +```ts +import { fromPartial } from "@total-typescript/shoehorn"; + +it("gets user by id", () => { + getUser( + fromPartial({ + body: { id: "123" }, + }), + ); +}); +``` + +### `as Type` → `fromPartial()` + +Before: + +```ts +getUser({ body: { id: "123" } } as Request); +``` + +After: + +```ts +import { fromPartial } from "@total-typescript/shoehorn"; + +getUser(fromPartial({ body: { id: "123" } })); +``` + +### `as unknown as Type` → `fromAny()` + +Before: + +```ts +getUser({ body: { id: 123 } } as unknown as Request); // wrong type on purpose +``` + +After: + +```ts +import { fromAny } from "@total-typescript/shoehorn"; + +getUser(fromAny({ body: { id: 123 } })); +``` + +## When to use each + +| Function | Use case | +| --------------- | -------------------------------------------------- | +| `fromPartial()` | Pass partial data that still type-checks | +| `fromAny()` | Pass intentionally wrong data (keeps autocomplete) | +| `fromExact()` | Force full object (swap with fromPartial later) | + +## Workflow + +1. **Gather requirements** - ask user: + - What test files have `as` assertions causing problems? + - Are they dealing with large objects where only some properties matter? + - Do they need to pass intentionally wrong data for error testing? + +2. **Install and migrate**: + - [ ] Install: `npm i @total-typescript/shoehorn` + - [ ] Find test files with `as` assertions: `grep -r " as [A-Z]" --include="*.test.ts" --include="*.spec.ts"` + - [ ] Replace `as Type` with `fromPartial()` + - [ ] Replace `as unknown as Type` with `fromAny()` + - [ ] Add imports from `@total-typescript/shoehorn` + - [ ] Run type check to verify diff --git a/.agents/skills/migrate-to-shoehorn/agents/openai.yaml b/.agents/skills/migrate-to-shoehorn/agents/openai.yaml new file mode 100644 index 0000000..3bd79ee --- /dev/null +++ b/.agents/skills/migrate-to-shoehorn/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Migrate to Shoehorn" + short_description: "Replace test assertions with shoehorn" diff --git a/.agents/skills/obsidian-vault/SKILL.md b/.agents/skills/obsidian-vault/SKILL.md new file mode 100644 index 0000000..9f867c4 --- /dev/null +++ b/.agents/skills/obsidian-vault/SKILL.md @@ -0,0 +1,16 @@ +--- +name: obsidian-vault +description: Search, create, and manage notes in the Obsidian vault with wikilinks and index notes. Use when user wants to find, create, or organize notes in Obsidian. +--- + +# Obsidian Vault + +Resolve the vault from the user's selected path, workspace configuration, or available Obsidian connection. If several vaults are plausible or none is configured, ask which one to use. Verify access before searching or writing; do not assume a personal machine path. + +Read the selected vault's instructions and nearby notes. Preserve its folder layout, naming, frontmatter, and index conventions instead of imposing a flat structure or title-case names. + +- Search filenames with `rg --files ` and content with `rg`, scoped to the selected vault. +- Use existing `[[wikilinks]]` conventions; check the destination note before adding a link. +- For a new note, search for existing coverage first, then add only relevant links and index entries. +- Before moving or renaming a note, check backlinks and update affected links within the requested scope. +- For read-only questions, return the relevant notes without reorganizing the vault. diff --git a/.agents/skills/obsidian-vault/agents/openai.yaml b/.agents/skills/obsidian-vault/agents/openai.yaml new file mode 100644 index 0000000..cb70f99 --- /dev/null +++ b/.agents/skills/obsidian-vault/agents/openai.yaml @@ -0,0 +1,3 @@ +interface: + display_name: "Obsidian Vault" + short_description: "Manage linked notes in Obsidian" diff --git a/.agents/skills/prototype/LOGIC.md b/.agents/skills/prototype/LOGIC.md index 5f5a3fd..292e624 100644 --- a/.agents/skills/prototype/LOGIC.md +++ b/.agents/skills/prototype/LOGIC.md @@ -1,15 +1,13 @@ # Logic Prototype -A single, self-contained HTML file — a **shareable demo** — that lets anyone drive a state model by clicking buttons. Use this when the question is about **business logic, state transitions, or data shape** — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases. - -Because it's one file with nothing to install, you can hand it to a non-developer — a designer, a PM, a domain expert — and let them feel the model for themselves. So it speaks their language, not the code's. +A tiny interactive terminal app that lets the user drive a state model by hand. Use this when the question is about **business logic, state transitions, or data shape** — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases. ## When this is the right shape - "I'm not sure if this state machine handles the edge case where X then Y." - "Does this data model actually let me represent the case where..." - "I want to feel out what the API should look like before writing it." -- Anything where someone wants to **press buttons and watch state change**. +- Anything where the user wants to **press buttons and watch state change**. If the question is "what should this look like" — wrong branch. Use [UI.md](UI.md). @@ -17,11 +15,17 @@ If the question is "what should this look like" — wrong branch. Use [UI.md](UI ### 1. State the question -Before writing code, write down what state model and what question you're prototyping. One paragraph, at the top of the demo (in a visible intro, not just a comment). A logic prototype that answers the wrong question is pure waste — make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK. +Before writing code, write down what state model and what question you're prototyping. One paragraph, in the prototype's README or a comment at the top of the file. A logic prototype that answers the wrong question is pure waste — make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK. -### 2. Isolate the logic in a portable module +### 2. Pick the language -Put the actual logic — the bit that's answering the question — in a single `