Compare commits
5 Commits
43280e4d4a
...
7d22ff0c02
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d22ff0c02 | |||
| 4da2452084 | |||
| 7550096968 | |||
| cbc03650d1 | |||
| 8ad93c7617 |
16
AGENTS.md
16
AGENTS.md
@@ -1,7 +1,7 @@
|
|||||||
# skills
|
# skills
|
||||||
|
|
||||||
Personal agent skills packaged through Nix.
|
Personal agent skills packaged through Nix.
|
||||||
The domain glossary lives at `$(xdg-user-dir DOCUMENTS)/ai-artifacts/projects/skills/CONTEXT.md`.
|
The domain glossary lives at `$(xdg-user-dir DOCUMENTS)/ai-artifacts/projects/skills/011-skills-context.md`.
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
@@ -18,3 +18,17 @@ The domain glossary lives at `$(xdg-user-dir DOCUMENTS)/ai-artifacts/projects/sk
|
|||||||
|
|
||||||
- `python3` is supplied by the default Nix dev shell, not the ambient environment.
|
- `python3` is supplied by the default Nix dev shell, not the ambient environment.
|
||||||
Enter `nix develop` before running Python-based project tools.
|
Enter `nix develop` before running Python-based project tools.
|
||||||
|
- A skill-local `GLOSSARY.md` is runtime reference for that skill and has no relationship to the project's AI-artifacts-vault context glossary.
|
||||||
|
- This repository owns packaged skill sources only.
|
||||||
|
Enabling, replacing, or removing them in a consumer repository such as dotfiles is outside its scope.
|
||||||
|
- `nix flake check` evaluates the Git snapshot and omits untracked skill files.
|
||||||
|
Use `nix flake check "path:$PWD"` to include a newly created skill before staging it.
|
||||||
|
- `$(xdg-user-dir DOCUMENTS)/ai-artifacts` is itself an Obsidian vault for AI-generated artifacts, distinct from the personal vault where AI-generated notes are forbidden.
|
||||||
|
- Project artifact filenames carry globally allocated identifiers.
|
||||||
|
Discover context and ADR artifacts through the AI artifacts vault convention rather than assuming fixed names or type directories.
|
||||||
|
- Do not turn opportunistic use of another skill into a declared dependency.
|
||||||
|
Invocation of an unrelated skill remains the agent's discretion unless the current skill directly requires its contract.
|
||||||
|
- Do not instruct a skill to execute synchronously.
|
||||||
|
In-process execution is the baseline, while isolation and parallelism belong to an external caller or runtime.
|
||||||
|
- When adapting an upstream skill, preserve mechanics the upstream skill leaves unspecified.
|
||||||
|
Customize only the surfaces required to fit the local workflow rather than turning incidental choices into new contracts.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ All reading of the wider AI-managed wiki goes through `/wiki` (step 3).
|
|||||||
Resolve the artifact project directory as `$(xdg-user-dir DOCUMENTS)/ai-artifacts/projects/<project>`, where `<project>` is the lowercase basename of the target directory.
|
Resolve the artifact project directory as `$(xdg-user-dir DOCUMENTS)/ai-artifacts/projects/<project>`, where `<project>` is the lowercase basename of the target directory.
|
||||||
Route the target through this ladder, first match wins:
|
Route the target through this ladder, first match wins:
|
||||||
|
|
||||||
- An artifact project directory containing `spec/` or `tasks/` → a project: follow [`project.md`](project.md).
|
- An artifact project directory containing flat `*-spec.md` or `*-task.md` artifacts → a project: follow [`project.md`](project.md).
|
||||||
- No rung matches → report that the target is not something consume knows how to read, write nothing, and stop.
|
- No rung matches → report that the target is not something consume knows how to read, write nothing, and stop.
|
||||||
|
|
||||||
Done when a branch file is selected, or consume has stopped on an unsupported target.
|
Done when a branch file is selected, or consume has stopped on an unsupported target.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# project branch
|
# project branch
|
||||||
|
|
||||||
How consume reads a project target, and what it clears afterward.
|
How consume reads a project target, and what it clears afterward.
|
||||||
Reached from step 1 of [`SKILL.md`](SKILL.md) when the target's artifact project directory contains `spec/` or `tasks/`.
|
Reached from step 1 of [`SKILL.md`](SKILL.md) when the target's flat artifact project directory contains `*-spec.md` or `*-task.md` artifacts.
|
||||||
|
|
||||||
## Read the source, mine the specs
|
## Read the source, mine the specs
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ Read it thoroughly enough to understand its current state and to surface the reu
|
|||||||
A gotcha usually carries two separable things — a transferable principle and a concrete repo-specific answer — so surface both.
|
A gotcha usually carries two separable things — a transferable principle and a concrete repo-specific answer — so surface both.
|
||||||
Step 4 of SKILL.md routes the principle to the pull channel and the residue to the target's push channel.
|
Step 4 of SKILL.md routes the principle to the pull channel and the residue to the target's push channel.
|
||||||
|
|
||||||
The artifact project's `spec/` and `tasks/` directories are the canonical record of *why* — the reasoning, trade-offs, and intent behind what the code became.
|
The artifact project's `*-spec.md` and `*-task.md` artifacts are the canonical record of *why* — the reasoning, trade-offs, and intent behind what the code became.
|
||||||
They are the richest source of the generalizable lessons, and cleanup destroys them, so mine their reasoning now or lose it.
|
They are the richest source of the generalizable lessons, and cleanup destroys them, so mine their reasoning now or lose it.
|
||||||
But they are not current fact: where a spec or task disagrees with the source, the source wins, and where one describes work later abandoned or changed, the source is what actually happened.
|
But they are not current fact: where a spec or task disagrees with the source, the source wins, and where one describes work later abandoned or changed, the source is what actually happened.
|
||||||
|
|
||||||
@@ -25,9 +25,8 @@ Do not create subdirectories there.
|
|||||||
|
|
||||||
## Cleanup
|
## Cleanup
|
||||||
|
|
||||||
After the notes are written (step 6 of SKILL.md), delete the spec and task files that were present under the artifact project's `spec/` and `tasks/` directories when you read the project in step 2 — the scaffolding this run consumed.
|
After the notes are written (step 6 of SKILL.md), delete the `*-spec.md` and `*-task.md` artifacts that were present in the flat artifact project directory when you read the project in step 2 — the scaffolding this run consumed.
|
||||||
A file added after that read is not swept up.
|
A file added after that read is not swept up.
|
||||||
Remove `spec/` or `tasks/` when the cleanup leaves it empty.
|
|
||||||
|
|
||||||
Deleting those files can strand references to them.
|
Deleting those files can strand references to them.
|
||||||
Scan the target and its artifact project directory for pointers to each file about to be deleted — in `AGENTS.md`, `CONTEXT.md`, ADRs, and sibling specs — and fix or remove each one, following the pointer wherever it lands rather than checking the push channel alone.
|
Scan the target and its artifact project directory for pointers to each file about to be deleted — in `AGENTS.md`, context artifacts, ADRs, and sibling specs — and fix or remove each one, following the pointer wherever it lands rather than checking the push channel alone.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# CONTEXT.md Format
|
# Context Artifact Format
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
|
|||||||
@@ -22,20 +22,20 @@ This holds even when another skill invoked grill: grill delivers its summary and
|
|||||||
## 1. Select the mode
|
## 1. Select the mode
|
||||||
|
|
||||||
Resolve the artifact root with `$(xdg-user-dir DOCUMENTS)/ai-artifacts`.
|
Resolve the artifact root with `$(xdg-user-dir DOCUMENTS)/ai-artifacts`.
|
||||||
Use the lowercase basename of the current working directory as `<project>`.
|
Use the lowercase basename of the current working directory as `<project>` and inspect `projects/<project>/` directly for its numbered `<project>-context.md` artifact.
|
||||||
The context file is `$(xdg-user-dir DOCUMENTS)/ai-artifacts/projects/<project>/CONTEXT.md`.
|
Read the artifact root's `AGENTS.md` before any artifact write.
|
||||||
Its sibling `adr/` directory owns the project's ADRs.
|
Create the project directory only when a write requires it.
|
||||||
Create the project directory and `adr/` only when a write requires them.
|
|
||||||
|
|
||||||
- **Domain Modeling Mode** — a context file was found.
|
- **Domain Modeling Mode** — exactly one context artifact was found.
|
||||||
Run the interview and maintain the project's domain model as terms settle (see [Domain Modeling Mode](#domain-modeling-mode)).
|
Run the interview and maintain the project's domain model as terms settle (see [Domain Modeling Mode](#domain-modeling-mode)).
|
||||||
- **Free Mode** — no context file, and the plan raises no project-specific vocabulary worth pinning down.
|
- **Free Mode** — no context artifact was found, and the plan raises no project-specific vocabulary worth pinning down.
|
||||||
Run the interview with no document side effects.
|
Run the interview with no document side effects.
|
||||||
- **Clarification** — no context file, but the plan introduces terms specific to this project that later work will need to use consistently — the kind of terms [`CONTEXT-FORMAT.md`](CONTEXT-FORMAT.md) admits, not general programming concepts.
|
- **Clarification** — no context artifact was found, but the plan introduces terms specific to this project that later work will need to use consistently — the kind of terms [`CONTEXT-FORMAT.md`](CONTEXT-FORMAT.md) admits, not general programming concepts.
|
||||||
Before interviewing, ask whether to create a glossary.
|
Before interviewing, ask whether to create a glossary.
|
||||||
If yes, create it at `$(xdg-user-dir DOCUMENTS)/ai-artifacts/projects/<project>/CONTEXT.md` and continue in Domain Modeling Mode.
|
If yes, allocate the next vault identifier through `.counter`, create `<NNN>-<project>-context.md`, and continue in Domain Modeling Mode.
|
||||||
If no, continue in Free Mode.
|
If no, continue in Free Mode.
|
||||||
|
|
||||||
|
Stop and report the conflicting paths if more than one matching context artifact exists.
|
||||||
Done when the interview is running in Domain Modeling Mode or Free Mode.
|
Done when the interview is running in Domain Modeling Mode or Free Mode.
|
||||||
|
|
||||||
## 2. Run the interview
|
## 2. Run the interview
|
||||||
@@ -66,9 +66,9 @@ Active only when step 1 selected this mode.
|
|||||||
|
|
||||||
### Glossary
|
### Glossary
|
||||||
|
|
||||||
As a term crystallizes, update the context file right then — do not batch these to the end.
|
As a term crystallizes, update the context artifact right then — do not batch these to the end.
|
||||||
When a settling term clashes with one already in the glossary, call it out and reconcile to a single canonical word.
|
When a settling term clashes with one already in the glossary, call it out and reconcile to a single canonical word.
|
||||||
Keep the file a glossary and nothing else: vocabulary and ubiquitous language, no implementation detail.
|
Keep the artifact a glossary and nothing else: vocabulary and ubiquitous language, no implementation detail.
|
||||||
Write it in the format of [`CONTEXT-FORMAT.md`](CONTEXT-FORMAT.md).
|
Write it in the format of [`CONTEXT-FORMAT.md`](CONTEXT-FORMAT.md).
|
||||||
|
|
||||||
### ADRs
|
### ADRs
|
||||||
@@ -80,7 +80,9 @@ Offer to record an architectural decision only when all three hold:
|
|||||||
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons.
|
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons.
|
||||||
|
|
||||||
If any of the three is missing, skip it.
|
If any of the three is missing, skip it.
|
||||||
A recorded decision lives in the sibling `adr/` directory, numbered one past the highest already there (`0001-slug.md`), and can be a single paragraph:
|
A recorded decision lives directly in the project's flat artifact directory.
|
||||||
|
Allocate its identifier through the vault-root `.counter` and name it `<NNN>-<scope-slug>-<decision-slug>-adr.md` according to the vault convention.
|
||||||
|
It can be a single paragraph:
|
||||||
|
|
||||||
> # {Short title of the decision}
|
> # {Short title of the decision}
|
||||||
>
|
>
|
||||||
|
|||||||
114
skills/prototype/LOGIC.md
Normal file
114
skills/prototype/LOGIC.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# Logic Prototype
|
||||||
|
|
||||||
|
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 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).
|
||||||
|
|
||||||
|
## Process
|
||||||
|
|
||||||
|
### 1. State the question
|
||||||
|
|
||||||
|
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. Pick the language
|
||||||
|
|
||||||
|
Use whatever the host project uses.
|
||||||
|
If the project has no obvious runtime (e.g. a docs repo), ask.
|
||||||
|
|
||||||
|
Match the project's existing conventions for tooling — don't add a new package manager or runtime just for the prototype.
|
||||||
|
|
||||||
|
### 3. Isolate the logic in a portable module
|
||||||
|
|
||||||
|
Put the actual logic — the bit that's answering the question — behind a small, pure interface that could be lifted out and dropped into the real codebase later.
|
||||||
|
The TUI around it is throwaway.
|
||||||
|
The logic module shouldn't be.
|
||||||
|
|
||||||
|
The right shape depends on the question:
|
||||||
|
|
||||||
|
- **A pure reducer** — `(state, action) => state`.
|
||||||
|
Good when actions are discrete events and state is a single value.
|
||||||
|
- **A state machine** — explicit states and transitions.
|
||||||
|
Good when "which actions are even legal right now" is part of the question.
|
||||||
|
- **A small set of pure functions** over a plain data type.
|
||||||
|
Good when there's no implicit current state — just transformations.
|
||||||
|
- **A class or module with a clear method surface** when the logic genuinely owns ongoing internal state.
|
||||||
|
|
||||||
|
Pick whichever shape best fits the question being asked, *not* whichever is easiest to wire to a TUI.
|
||||||
|
Keep it pure: no I/O, no terminal code, no `console.log` for control flow.
|
||||||
|
The TUI imports it and calls into it.
|
||||||
|
Nothing flows the other direction.
|
||||||
|
|
||||||
|
This is what makes the prototype useful past its own lifetime: when the question's been answered, the validated reducer / machine / function set can be lifted into the real module on its own.
|
||||||
|
|
||||||
|
### 4. Build the smallest TUI that exposes the state
|
||||||
|
|
||||||
|
Build it as a **lightweight TUI** — on every tick, clear the screen (`console.clear()` / `print("\033[2J\033[H")` / equivalent) and re-render the whole frame.
|
||||||
|
The user should always see one stable view, not an ever-growing scrollback.
|
||||||
|
|
||||||
|
Each frame has two parts, in this order:
|
||||||
|
|
||||||
|
1. **Current state**, pretty-printed and diff-friendly (one field per line, or formatted JSON).
|
||||||
|
Use **bold** for field names or section headers and **dim** for less important context (timestamps, IDs, derived values).
|
||||||
|
Native ANSI escape codes are fine — `\x1b[1m` bold, `\x1b[2m` dim, `\x1b[0m` reset.
|
||||||
|
No need to pull in a styling library unless one is already in the project.
|
||||||
|
2. **Keyboard shortcuts**, listed at the bottom: `[a] add user [d] delete user [t] tick clock [q] quit`.
|
||||||
|
Bold the key, dim the description, or vice-versa — whatever reads cleanly.
|
||||||
|
|
||||||
|
Behaviour:
|
||||||
|
|
||||||
|
1. **Initialise state** — a single in-memory object/struct.
|
||||||
|
Render the first frame on start.
|
||||||
|
2. **Read one keystroke (or one line)** at a time, dispatch to a handler that mutates state.
|
||||||
|
3. **Re-render** the full frame after every action — don't append, replace.
|
||||||
|
4. **Loop until quit.**
|
||||||
|
|
||||||
|
The whole frame should fit on one screen.
|
||||||
|
|
||||||
|
### 5. Make it runnable in one command
|
||||||
|
|
||||||
|
Add a script to the project's existing task runner (`package.json` scripts, `Makefile`, `justfile`, `pyproject.toml`).
|
||||||
|
The user should run `pnpm run <prototype-name>` or equivalent — never need to remember a path.
|
||||||
|
|
||||||
|
If the host project has no task runner, just put the command at the top of the prototype's README.
|
||||||
|
|
||||||
|
### 6. Hand it over
|
||||||
|
|
||||||
|
Give the user the run command.
|
||||||
|
They'll drive it themselves.
|
||||||
|
The interesting moments are when they say "wait, that shouldn't be possible" or "huh, I assumed X would be different" — those are the bugs in the _idea_, which is the whole point.
|
||||||
|
If they want new actions added, add them.
|
||||||
|
Prototypes evolve.
|
||||||
|
|
||||||
|
### 7. Capture the answer and the prototype
|
||||||
|
|
||||||
|
Once the prototype has answered its question, capture the answer, then capture the prototype the way the [SKILL](SKILL.md) describes.
|
||||||
|
When the caller permits implementation, the validated reducer, machine, or function set lifts into the real module as the absorbed decision.
|
||||||
|
A planning-only caller leaves the real module unchanged.
|
||||||
|
The TUI shell rides along to the throwaway branch that keeps the prototype as a primary source.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
|
||||||
|
- **Don't add tests.**
|
||||||
|
A prototype that needs tests is no longer a prototype.
|
||||||
|
- **Don't wire it to the real database.**
|
||||||
|
Use an in-memory store unless the question is specifically about persistence.
|
||||||
|
- **Don't generalise.**
|
||||||
|
No "what if we wanted to support X later."
|
||||||
|
The prototype answers one question.
|
||||||
|
- **Don't blur the logic and the TUI together.**
|
||||||
|
If the reducer / state machine references `console.log`, prompts, or terminal escape codes, it's no longer portable.
|
||||||
|
Keep the TUI as a thin shell over a pure module.
|
||||||
|
- **Don't ship the TUI shell into production.**
|
||||||
|
The shell is optimised for being driven by hand from a terminal.
|
||||||
|
The logic module behind it is the bit worth keeping.
|
||||||
47
skills/prototype/SKILL.md
Normal file
47
skills/prototype/SKILL.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
name: prototype
|
||||||
|
description: Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Prototype
|
||||||
|
|
||||||
|
A prototype is **throwaway code that answers a question**.
|
||||||
|
The question decides the shape.
|
||||||
|
|
||||||
|
## Pick a branch
|
||||||
|
|
||||||
|
Identify which question is being answered — from the user's prompt, the surrounding code, or by asking if the user is around:
|
||||||
|
|
||||||
|
- **"Does this logic / state model feel right?"** → [LOGIC.md](LOGIC.md).
|
||||||
|
Build a tiny interactive terminal app that pushes the state machine through cases that are hard to reason about on paper.
|
||||||
|
- **"What should this look like?"** → [UI.md](UI.md).
|
||||||
|
Generate several radically different UI variations on a single route, switchable via a URL search param and a floating bottom bar.
|
||||||
|
|
||||||
|
The two branches produce very different artifacts — getting this wrong wastes the whole prototype.
|
||||||
|
If the question is genuinely ambiguous and the user isn't reachable, default to whichever branch better matches the surrounding code (a backend module → logic, a page or component → UI) and state the assumption at the top of the prototype.
|
||||||
|
|
||||||
|
## Rules that apply to both
|
||||||
|
|
||||||
|
1. **Throwaway from day one, and clearly marked as such.**
|
||||||
|
Locate the prototype code close to where it will actually be used (next to the module or page it's prototyping for) so context is obvious — but name it so a casual reader can see it's a prototype, not production.
|
||||||
|
For throwaway UI routes, obey whatever routing convention the project already uses.
|
||||||
|
Don't invent a new top-level structure.
|
||||||
|
2. **One command to run.**
|
||||||
|
Whatever the project's existing task runner supports — `pnpm <name>`, `python <path>`, `bun <path>`, etc.
|
||||||
|
The user must be able to start it without thinking.
|
||||||
|
3. **No persistence by default.**
|
||||||
|
State lives in memory.
|
||||||
|
Persistence is the thing the prototype is _checking_, not something it should depend on.
|
||||||
|
If the question explicitly involves a database, hit a scratch DB or a local file with a clear "PROTOTYPE — wipe me" name.
|
||||||
|
4. **Skip the polish.**
|
||||||
|
No tests, no error handling beyond what makes the prototype _runnable_, no abstractions.
|
||||||
|
The point is to learn something fast.
|
||||||
|
5. **Surface the state.**
|
||||||
|
After every action (logic) or on every variant switch (UI), print or render the full relevant state so the user can see what changed.
|
||||||
|
6. **Capture it when done.**
|
||||||
|
When the caller permits implementation, fold any validated decision into the real code.
|
||||||
|
A planning-only caller such as Wayfinder stops at the verdict.
|
||||||
|
Commit the prototype itself to a throwaway branch, out of main, as a **primary source**.
|
||||||
|
Write a Prototype artifact under `$(xdg-user-dir DOCUMENTS)/ai-artifacts/projects/<project>/`, where `<project>` is the lowercase basename of the current working directory, following the vault's `AGENTS.md`.
|
||||||
|
Include the question, a context pointer to the branch, run instructions, and the verdict, plus screenshots and useful code snippets where they help preserve the result.
|
||||||
|
The main branch keeps only a validated decision that the caller permitted the skill to fold in.
|
||||||
145
skills/prototype/UI.md
Normal file
145
skills/prototype/UI.md
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
# UI Prototype
|
||||||
|
|
||||||
|
Generate **several radically different UI variations** on a single route, switchable from a floating bottom bar.
|
||||||
|
The user flips between variants in the browser, picks one (or steals bits from each), then throws the rest away.
|
||||||
|
|
||||||
|
If the question is about logic/state rather than what something looks like — wrong branch.
|
||||||
|
Use [LOGIC.md](LOGIC.md).
|
||||||
|
|
||||||
|
## When this is the right shape
|
||||||
|
|
||||||
|
- "What should this page look like?"
|
||||||
|
- "I want to see a few options for this dashboard before committing."
|
||||||
|
- "Try a different layout for the settings screen."
|
||||||
|
- Any time the user would otherwise spend a day picking between three vague mockups in their head.
|
||||||
|
|
||||||
|
## Two sub-shapes — strongly prefer sub-shape A
|
||||||
|
|
||||||
|
A UI prototype is much easier to judge when it's **butting up against the rest of the app** — real header, real sidebar, real data, real density.
|
||||||
|
A throwaway route on its own is a vacuum: every variant looks fine in isolation.
|
||||||
|
Default to sub-shape A whenever there's a plausible existing page to host the variants.
|
||||||
|
Only reach for sub-shape B if the prototype genuinely has no nearby home.
|
||||||
|
|
||||||
|
### Sub-shape A — adjustment to an existing page (preferred)
|
||||||
|
|
||||||
|
The route already exists.
|
||||||
|
Variants are rendered **on the same route**, gated by a `?variant=` URL search param.
|
||||||
|
The existing data fetching, params, and auth all stay — only the rendering swaps.
|
||||||
|
This is the default.
|
||||||
|
Pick it unless there's a specific reason not to.
|
||||||
|
|
||||||
|
If the prototype is for something that doesn't yet have a page but *would naturally live inside one* (a new section of the dashboard, a new card on the settings screen, a new step in an existing flow) — that's still sub-shape A.
|
||||||
|
Mount the variants inside the host page.
|
||||||
|
|
||||||
|
### Sub-shape B — a new page (last resort)
|
||||||
|
|
||||||
|
Only use this when the thing being prototyped genuinely has no existing page to live inside — e.g. an entirely new top-level surface, or a flow that can't be embedded anywhere sensible.
|
||||||
|
|
||||||
|
Create a **throwaway route** following whatever routing convention the project already uses — don't invent a new top-level structure.
|
||||||
|
Name it so it's obviously a prototype (e.g. include the word `prototype` in the path or filename).
|
||||||
|
Same `?variant=` pattern.
|
||||||
|
|
||||||
|
Before committing to sub-shape B, sanity-check: is there really no existing page this could be embedded in?
|
||||||
|
An empty route hides design problems that a populated one would expose.
|
||||||
|
|
||||||
|
In both sub-shapes the floating bottom bar is identical.
|
||||||
|
|
||||||
|
## Process
|
||||||
|
|
||||||
|
### 1. State the question and pick N
|
||||||
|
|
||||||
|
Default to **3 variants**.
|
||||||
|
More than 5 stops being radically different and starts being noise — cap there.
|
||||||
|
|
||||||
|
Write down the plan in one line, in the prototype's location or a top-of-file comment:
|
||||||
|
|
||||||
|
> "Three variants of the settings page, switchable via `?variant=`, on the existing `/settings` route."
|
||||||
|
|
||||||
|
This works whether the user is here to push back or not.
|
||||||
|
|
||||||
|
### 2. Generate radically different variants
|
||||||
|
|
||||||
|
Draft each variant.
|
||||||
|
Hold each one to:
|
||||||
|
|
||||||
|
- The page's purpose and the data it has access to.
|
||||||
|
- The project's component library / styling system (TailwindCSS, shadcn, MUI, plain CSS, whatever).
|
||||||
|
- A clear exported component name, e.g. `VariantA`, `VariantB`, `VariantC`.
|
||||||
|
|
||||||
|
Variants must be **structurally different** — different layout, different information hierarchy, different primary affordance, not just different colours.
|
||||||
|
Three slightly-tweaked card grids isn't a UI prototype, it's wallpaper.
|
||||||
|
If two drafts come out too similar, redo one with explicit "do not use a card grid" guidance.
|
||||||
|
|
||||||
|
### 3. Wire them together
|
||||||
|
|
||||||
|
Create a single switcher component on the route:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// pseudo-code — adapt to the project's framework
|
||||||
|
const variant = searchParams.get('variant') ?? 'A';
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{variant === 'A' && <VariantA {...data} />}
|
||||||
|
{variant === 'B' && <VariantB {...data} />}
|
||||||
|
{variant === 'C' && <VariantC {...data} />}
|
||||||
|
<PrototypeSwitcher variants={['A','B','C']} current={variant} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
For sub-shape A (existing page): keep all the existing data fetching above the switcher.
|
||||||
|
Only the rendered subtree changes per variant.
|
||||||
|
|
||||||
|
For sub-shape B (new page): the throwaway route under `/prototype/<name>` mounts the same switcher.
|
||||||
|
|
||||||
|
### 4. Build the floating switcher
|
||||||
|
|
||||||
|
A small fixed-position bar at the bottom-centre of the screen with three pieces:
|
||||||
|
|
||||||
|
- **Left arrow** — cycles to the previous variant (wraps around).
|
||||||
|
- **Variant label** — shows the current variant key and, if the variant exports a name, that name too. e.g. `B — Sidebar layout`.
|
||||||
|
- **Right arrow** — cycles forward (wraps around).
|
||||||
|
|
||||||
|
Behaviour:
|
||||||
|
|
||||||
|
- Clicking an arrow updates the URL search param (use the framework's router — `router.replace` on Next, `navigate` on React Router, etc) so the variant is shareable and reload-stable.
|
||||||
|
- Keyboard: `←` and `→` arrow keys also cycle.
|
||||||
|
Don't intercept arrow keys when an `<input>`, `<textarea>`, or `[contenteditable]` is focused.
|
||||||
|
- Visually distinct from the page (e.g. high-contrast pill, subtle shadow) so it's obviously not part of the design being evaluated.
|
||||||
|
- Hidden in production builds — gate on `process.env.NODE_ENV !== 'production'` or an equivalent check, so a stray prototype merge can't ship the bar to users.
|
||||||
|
|
||||||
|
Put the switcher in a single shared component so both sub-shapes can reuse it.
|
||||||
|
Locate it wherever shared UI lives in the project.
|
||||||
|
|
||||||
|
### 5. Hand it over
|
||||||
|
|
||||||
|
Surface the URL (and the `?variant=` keys).
|
||||||
|
The user will flip through whenever they get to it.
|
||||||
|
The interesting feedback is usually **"I want the header from B with the sidebar from C"** — that's the actual design they want.
|
||||||
|
|
||||||
|
### 6. Capture the answer and clean up
|
||||||
|
|
||||||
|
Once a variant has won, capture the answer — which variant and why — then capture the prototype the way the [SKILL](SKILL.md) describes.
|
||||||
|
When the caller permits implementation, fold the winner into the real code and move the rest onto the throwaway branch, not into main:
|
||||||
|
|
||||||
|
- **Sub-shape A** — fold the winner into the existing page and drop the losing variants and switcher from main.
|
||||||
|
- **Sub-shape B** — promote the winning variant to a real route and drop the throwaway route and switcher from main.
|
||||||
|
|
||||||
|
A planning-only caller leaves the real route unchanged.
|
||||||
|
The full set of variants is the primary source, so it lands on the throwaway branch, not the bin — variant components and the switcher left in the main branch rot fast and confuse the next reader.
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
|
||||||
|
- **Variants that differ only in colour or copy.**
|
||||||
|
That's a tweak, not a prototype.
|
||||||
|
Real variants disagree about structure.
|
||||||
|
- **Sharing too much code between variants.**
|
||||||
|
A shared `<Header>` is fine.
|
||||||
|
A shared `<Layout>` defeats the point.
|
||||||
|
Each variant should be free to throw out the layout.
|
||||||
|
- **Wiring variants to real mutations.**
|
||||||
|
Read-only prototypes are fine.
|
||||||
|
If a variant needs to mutate, point it at a stub — the question is "what should this look like", not "does the backend work".
|
||||||
|
- **Promoting the prototype directly to production.**
|
||||||
|
The variant code was written under prototype constraints (no tests, minimal error handling).
|
||||||
|
Rewrite it properly when you fold it in.
|
||||||
14
skills/research/SKILL.md
Normal file
14
skills/research/SKILL.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: research
|
||||||
|
description: Investigate a question against high-trust primary sources and capture the findings as a Markdown file in the AI artifacts vault. Use when the user wants a topic researched, docs or API facts gathered, or reading legwork delegated to a background agent.
|
||||||
|
---
|
||||||
|
|
||||||
|
Spin up a **background agent** to do the research, so you keep working while it reads.
|
||||||
|
|
||||||
|
Its job:
|
||||||
|
|
||||||
|
1. Investigate the question against **primary sources** — official docs, source code, specs, first-party APIs — not a secondary write-up of them.
|
||||||
|
Follow every claim back to the source that owns it.
|
||||||
|
2. Write the findings to a single Markdown file, citing each claim's source.
|
||||||
|
3. Save it under `$(xdg-user-dir DOCUMENTS)/ai-artifacts/projects/<project>/`, where `<project>` is the lowercase basename of the current working directory.
|
||||||
|
Read the vault's `AGENTS.md` and follow its artifact conventions.
|
||||||
132
skills/wayfinder/ARTIFACTS.md
Normal file
132
skills/wayfinder/ARTIFACTS.md
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# Wayfinder artifacts
|
||||||
|
|
||||||
|
## Resolve the project
|
||||||
|
|
||||||
|
Resolve the vault through `$(xdg-user-dir DOCUMENTS)/ai-artifacts` and read its `AGENTS.md` before any artifact write.
|
||||||
|
Use the lowercase basename of the current working directory as the project slug.
|
||||||
|
Create `projects/<project>/` when a new project first needs a map.
|
||||||
|
Keep the project directory flat.
|
||||||
|
|
||||||
|
Allocate every new artifact through the vault-root `.counter`.
|
||||||
|
The coordinating Wayfinder agent reconciles duplicate identifiers after concurrent workers return.
|
||||||
|
|
||||||
|
## Names
|
||||||
|
|
||||||
|
Use an effort slug that identifies one durable effort and is not reused for another map in the project.
|
||||||
|
Use these filenames:
|
||||||
|
|
||||||
|
- Map: `<NNN>-<effort-slug>-map.md`
|
||||||
|
- Ticket: `<NNN>-<effort-slug>-<subject-slug>-ticket.md`
|
||||||
|
- Research result: `<NNN>-<effort-slug>-<subject-slug>-research.md`
|
||||||
|
- Prototype result: `<NNN>-<effort-slug>-<subject-slug>-prototype.md`
|
||||||
|
|
||||||
|
Refer to artifacts through bare Obsidian wikilinks such as `[[042-wayfinder-session-auth-ticket]]`.
|
||||||
|
Never use a bare identifier as a human-facing reference.
|
||||||
|
|
||||||
|
## Map
|
||||||
|
|
||||||
|
The map is the effort's root artifact and has no `parent`.
|
||||||
|
It is an index rather than the store for ticket resolutions.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# <effort name>
|
||||||
|
|
||||||
|
## Destination
|
||||||
|
|
||||||
|
<one or two lines describing what reaching the end of this map looks like>
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
<standing domain, skill, and execution guidance>
|
||||||
|
|
||||||
|
## Frontier
|
||||||
|
|
||||||
|
- [[<open-unblocked-unclaimed-ticket>]]
|
||||||
|
|
||||||
|
## Resolutions so far
|
||||||
|
|
||||||
|
<one entry per resolved ticket, linking to the canonical result>
|
||||||
|
|
||||||
|
## Not yet specified
|
||||||
|
|
||||||
|
<in-scope fog that is not precise enough to ticket>
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
<work consciously ruled beyond the destination>
|
||||||
|
```
|
||||||
|
|
||||||
|
The Frontier is a derived navigation index.
|
||||||
|
Ticket metadata is authoritative.
|
||||||
|
Repair the Frontier whenever it is missing, stale, or inconsistent with ticket state.
|
||||||
|
Order Frontier links by artifact identifier unless the user chooses another ticket.
|
||||||
|
|
||||||
|
For a resolved Grill or Task ticket, link the ticket and give a one-line gist.
|
||||||
|
For a resolved Research or Prototype ticket, give the ticket's gist once and nest links to every result artifact beneath it.
|
||||||
|
Do not copy the resolution into the map.
|
||||||
|
|
||||||
|
## Tickets
|
||||||
|
|
||||||
|
A new ticket starts as:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
status: open
|
||||||
|
parent: "[[<map-or-surfacing-ticket>]]"
|
||||||
|
blocked-by: []
|
||||||
|
tags:
|
||||||
|
- ticket/<type>
|
||||||
|
---
|
||||||
|
|
||||||
|
# <ticket name>
|
||||||
|
|
||||||
|
## Question
|
||||||
|
|
||||||
|
<one precise question or prerequisite action sized to one agent session>
|
||||||
|
```
|
||||||
|
|
||||||
|
Use one of these tags:
|
||||||
|
|
||||||
|
- `ticket/research`
|
||||||
|
- `ticket/prototype`
|
||||||
|
- `ticket/grill`
|
||||||
|
- `ticket/task/afk`
|
||||||
|
- `ticket/task/human`
|
||||||
|
|
||||||
|
`parent` records provenance.
|
||||||
|
An initial ticket points to the map.
|
||||||
|
A ticket surfaced by another ticket points to the surfacing ticket.
|
||||||
|
An artifact may have many children, which agents find by searching for backlinks to its wikilink.
|
||||||
|
|
||||||
|
`blocked-by` records zero or more upstream artifacts that must resolve before the ticket becomes actionable.
|
||||||
|
It is independent of `parent`.
|
||||||
|
A ticket blocker is satisfied when its status is `resolved`.
|
||||||
|
A non-ticket result blocker is satisfied when its artifact exists.
|
||||||
|
A ticket is on the Frontier when its status is `open`, every blocker is satisfied, and it has no claim.
|
||||||
|
|
||||||
|
## Claims and status
|
||||||
|
|
||||||
|
Ticket status is one of:
|
||||||
|
|
||||||
|
- `open`
|
||||||
|
- `claimed`
|
||||||
|
- `resolved`
|
||||||
|
- `out-of-scope`
|
||||||
|
|
||||||
|
Claim a ticket by setting `status: claimed`, `claimed-by` to the current execution-session identifier, and `claimed-at` to the current timestamp before doing any work.
|
||||||
|
Use `PI_SESSION_ID` when available and an equivalent harness session identifier otherwise.
|
||||||
|
Claims do not expire automatically.
|
||||||
|
The acting agent uses the available context to recover an abandoned claim.
|
||||||
|
|
||||||
|
Only resolved tickets appear under Resolutions so far.
|
||||||
|
An out-of-scope ticket is closed and linked from Out of scope with the reason it lies beyond the destination.
|
||||||
|
|
||||||
|
## Results
|
||||||
|
|
||||||
|
A Grill or Task ticket stores its canonical result under a `## Resolution` section in that ticket.
|
||||||
|
Research and Prototype tickets leave their question in the ticket and store results in one or more child artifacts whose `parent` points to the ticket.
|
||||||
|
The called skill creates those result artifacts but does not edit the ticket or map.
|
||||||
|
The coordinating Wayfinder agent integrates the artifacts, marks the ticket resolved, and updates the map.
|
||||||
|
|
||||||
|
Navigate the artifact journey forward by finding every note whose `parent` links to the current artifact.
|
||||||
|
Do not duplicate those relationships through per-artifact Next sections.
|
||||||
97
skills/wayfinder/SKILL.md
Normal file
97
skills/wayfinder/SKILL.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
name: wayfinder
|
||||||
|
description: Plan work too large and uncertain for one agent session as a durable map of decision tickets, then resolve one frontier ticket at a time until the route to the destination is clear.
|
||||||
|
disable-model-invocation: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# wayfinder
|
||||||
|
|
||||||
|
A loose idea has arrived that is too large for one agent session and too foggy to plan directly.
|
||||||
|
Wayfinding charts the decisions needed to reach a **destination**, then works those decisions one at a time until the route is clear.
|
||||||
|
|
||||||
|
Read [`ARTIFACTS.md`](ARTIFACTS.md) before charting or working a map.
|
||||||
|
It is the sole source for how maps, tickets, claims, blocking, results, and the Frontier live in the AI artifacts vault.
|
||||||
|
|
||||||
|
## Plan, don't do
|
||||||
|
|
||||||
|
Wayfinder plans by default.
|
||||||
|
The map is complete when nothing remains to decide before someone performs the destination work.
|
||||||
|
The urge to implement the destination usually marks the edge of the map.
|
||||||
|
An effort may explicitly permit execution in its Notes, but otherwise preserve resolutions and hand off rather than deliver the destination.
|
||||||
|
|
||||||
|
## Ticket types
|
||||||
|
|
||||||
|
Every ticket is either **HITL**, worked through a live exchange with the human, or **AFK**, driven by the agent.
|
||||||
|
A HITL ticket never resolves by having the agent speak for the human.
|
||||||
|
|
||||||
|
- **Research** (AFK): Investigate knowledge outside the current working directory through the `research` skill.
|
||||||
|
- **Prototype** (HITL): Create concrete Logic or UI code to react to through the `prototype` skill.
|
||||||
|
Within Wayfinder, stop at the verdict rather than folding the result into production.
|
||||||
|
- **Grill** (HITL): Resolve a decision through the `grill` skill.
|
||||||
|
This is the default ticket type.
|
||||||
|
- **Task** (AFK or HITL): Perform prerequisite work that must happen before a decision can be made.
|
||||||
|
The required capability is specific to the action.
|
||||||
|
The agent performs it where possible and otherwise gives the human a precise checklist.
|
||||||
|
|
||||||
|
A Task earns its place by unblocking a decision, not by delivering part of the destination.
|
||||||
|
|
||||||
|
## Fog of war
|
||||||
|
|
||||||
|
The map is deliberately incomplete.
|
||||||
|
**Not yet specified** holds in-scope questions that are visible but cannot yet be stated precisely enough to ticket.
|
||||||
|
Create a ticket as soon as its question is precise, even when it is blocked and cannot yet be answered.
|
||||||
|
A fog entry may graduate into several tickets or disappear when an earlier resolution changes the route.
|
||||||
|
|
||||||
|
The destination fixes scope.
|
||||||
|
Work beyond it belongs in **Out of scope**, never in fog.
|
||||||
|
If an existing ticket proves to be beyond the destination, close it as out of scope and link it from that section rather than recording it as a resolution.
|
||||||
|
|
||||||
|
## Select the mode
|
||||||
|
|
||||||
|
- A loose idea without a map uses **Chart the map**.
|
||||||
|
- An existing map uses **Work through the map**.
|
||||||
|
|
||||||
|
Never resolve more than one non-Research ticket in a session.
|
||||||
|
|
||||||
|
## Chart the map
|
||||||
|
|
||||||
|
1. **Name the destination.**
|
||||||
|
Invoke `grill` to settle what reaching the end of this effort looks like.
|
||||||
|
Done when the destination states the spec, decision, or change the map is finding its way toward and fixes its scope.
|
||||||
|
2. **Map breadth-first.**
|
||||||
|
Invoke `grill` again to fan out across the whole space, identifying precise open questions, blocking relationships, and fog without drilling into any one answer.
|
||||||
|
If this surfaces no fog and the route fits one session, stop and ask how the user wants to proceed instead of creating a map.
|
||||||
|
Done when every visible in-scope uncertainty is either a precise ticket question or an honest fog entry.
|
||||||
|
3. **Create the map and tickets.**
|
||||||
|
Create the map first, then every currently precise ticket, then wire `blocked-by` relationships in a second pass according to `ARTIFACTS.md`.
|
||||||
|
Done when the map is the effort root, every precise question has one ticket, every known blocking edge is represented, and the Frontier is current.
|
||||||
|
4. **Dispatch Research.**
|
||||||
|
Invoke `research` for each Research ticket using whatever isolation or concurrency the caller provides.
|
||||||
|
Let the coordinating Wayfinder agent integrate returned Research artifacts according to `ARTIFACTS.md`.
|
||||||
|
Done when every dispatched Research result has been integrated or every Research ticket that could not run remains open with the reason visible.
|
||||||
|
5. Stop without resolving a HITL ticket.
|
||||||
|
|
||||||
|
## Work through the map
|
||||||
|
|
||||||
|
1. **Orient.**
|
||||||
|
Read the map at low resolution and reconcile its derived Frontier against ticket metadata.
|
||||||
|
Done when the destination, standing Notes, prior resolutions, fog, scope boundary, and current Frontier agree with the artifacts.
|
||||||
|
2. **Claim one ticket.**
|
||||||
|
Use the user-named ticket when it is actionable, otherwise claim the first Frontier ticket.
|
||||||
|
Persist the claim before doing its work.
|
||||||
|
Done when exactly one open, unblocked ticket records this session's claim.
|
||||||
|
3. **Resolve by type.**
|
||||||
|
Invoke `research`, `prototype`, or `grill` for those ticket types.
|
||||||
|
Perform a Task through the capability or human checklist it requires.
|
||||||
|
Zoom into related artifacts only as needed rather than loading the whole effort.
|
||||||
|
Done when the ticket's question has a resolution or the prerequisite Task is complete.
|
||||||
|
4. **Record the resolution.**
|
||||||
|
Persist the result, resolve the ticket, and add its gist and links under the map's Resolutions so far.
|
||||||
|
Done when the resolution lives in exactly one canonical place and the map points to it without restating it.
|
||||||
|
5. **Advance the frontier.**
|
||||||
|
Create tickets surfaced by the resolution according to `ARTIFACTS.md`.
|
||||||
|
Graduate newly precise fog, remove invalidated tickets, move beyond-destination work out of scope, and recompute the Frontier.
|
||||||
|
Done when every newly visible question has exactly one home and the map matches all current ticket metadata.
|
||||||
|
|
||||||
|
Expect concurrent sessions to edit the same effort.
|
||||||
|
Re-read shared artifacts before each write and reconcile collisions through the vault convention.
|
||||||
Reference in New Issue
Block a user