Updated 2026-06-25: the plugin was renamed to wherefore and gained the resolve and supersede skills since this was first published.
Three people hashed out the API structure in a meeting back in March. The conclusion is in their heads. Nowhere else.
This happens constantly in software teams. Some decisions make it into a Jira ticket, a PR description, or an ADR that someone spent an hour writing and that nobody will read again. Most don't. A few months later, someone asks why the thing is built the way it is, and the honest answer is that you weren't in the room.
The meetings aren't the problem. The reasoning disappearing afterward is.
I built a Claude Code skill to capture this: a minimal decision log that lives in the project repository, gets populated during working sessions with Claude, and can be queried later when someone needs to know why. Open and self-hosted at github.com/DustinVK/wherefore.
Four skills and a command
The plugin ships four skills and a setup command.
capture logs a decision from a discussion. ask retrieves prior context: invoke it when you're about to touch a system and want to know what was decided there before. resolve closes an open question left hanging in an earlier entry, recording the answer and rationale. supersede retires a past decision after the fact, marking it obsolete or forwarding to a replacement, without needing a full new session.
Capture and ask have different triggers: you invoke capture at the end of a session where something got decided, ask when you're about to touch a system and want prior context. Neither tries to do the other's job.
If you use a /grill-me style workflow to stress-test a decision before committing to it, capture is a natural next step. The grilling surfaces what was considered and rejected; the log preserves that output in a form you can actually find later.
What an entry looks like
The goal is not a transcript. A transcript of a technical discussion is 4,000 words of context-switching and tangents that resolves, somewhere near the end, into a paragraph of actual decisions. The entry should be that paragraph.
Each log file captures four things:
- What was decided and the outcome.
- Why that choice was made.
- What was rejected and why each option was ruled out.
- Open questions left unresolved, if any.
A typical entry is under 40 lines. Frontmatter carries the metadata; freeform markdown carries the content. Here is the shape of one:
---
date: 2026-06-22
title: OAuth token encryption — KMS-direct, SSM for app credentials
areas: [ingestion, credentials]
topics: [security, kms, oauth]
status: active
---
## Summary
Per-location GBP refresh tokens are stored as KMS-encrypted ciphertext in the
`locations` table. App-level OAuth credentials (client ID + secret) live in SSM
Parameter Store. No envelope encryption; no cross-invocation credential caching.
## Decisions
- **Per-location refresh tokens:** using `KMS.Encrypt` / `KMS.Decrypt` with encryption context `{"location_id": id}`.
One KMS decrypt per job invocation; no data-key caching.
- **App credentials (client ID/secret):** SSM Parameter Store SecureString, fetched
at cold start. Free, KMS-backed at rest, no per-secret monthly fee.
- **Access-token caching:** cache the short-lived access token in `sync.Map`; never
cache the decrypted refresh token. Cache hits transitively skip the KMS call.
## Why
Postgres disk encryption protects a stolen disk, not anyone who can read the table —
a refresh token is effectively a long-lived password and must be encrypted at rest
behind a real key boundary. Co-locating the ciphertext with the location row is the
correct data model. Secrets Manager would charge $0.40/location/month for rotation
we don't use. KMS-direct costs ~$1–3/month at hundreds of locations.
Access-token caching (not refresh-token caching) gives the best risk/reward profile:
a ~1h self-expiring credential sits in process memory; the refresh token is decrypted
transiently per job and discarded. Caching the refresh token would hold the long-lived
credential for the container's entire lifetime — maximum blast radius — to save cents.
## Alternatives considered
**Envelope encryption (AWS Encryption SDK or hand-rolled AES-GCM):** envelope helps
when payloads exceed KMS's 4KB limit or when one data key is amortized across a batch.
Neither applies: a refresh token is a few hundred bytes; the worker processes exactly
one location per invocation. Envelope would make the same single KMS call plus an
AES-GCM nonce-management layer on top — strictly more code, zero savings.
**Secrets Manager per location:** correct security model, but $0.40/secret/month scales
linearly with location count. Rejected in favor of encrypted column.
**Cached data key across warm invocations:** holds a plaintext key that unlocks every
location's token in memory for the container's lifetime. Rejected — maximum blast radius
to save a sub-$3/month KMS bill.
## Open questions
None. The KMS CMK is wired via `sst.Linkable` (couples ARN injection with the IAM
grant so they can't drift); the Go implementation of the decrypt path is pending `gbp.go`.
The skill instructs Claude to extract decisions and reasoning and drop everything else. If nothing was actually decided in a session, open questions go under that heading rather than manufacturing a conclusion that was not there.
The tagging system
Entries are tagged along two dimensions: areas and topics.
Areas are feature slices or product domains. The what. Something like order-process or user-auth or reporting. Topics are cross-cutting technical concerns. The how. Something like performance or caching or third-party-integration.
The distinction matters for retrieval. If someone asks what decisions were made about caching, they want everything tagged with the topic caching, regardless of which feature it touches. "What about the checkout flow?" lands differently: everything in the area checkout, whatever the technical concern. The two dimensions let you slice it either way.
The controlled vocabulary is the part that actually makes this work. Early in a project, you populate a topics.md file with the
tags that matter for your codebase. From that point on, the skill reuses existing tags rather than inventing new ones. This sounds
like a minor detail. It is not. Uncontrolled tagging fragments search results across synonyms and near-duplicates within a few
months. The plugin includes a /wherefore:seed command that bootstraps the vocabulary from your codebase structure, so you're not
starting from a blank page.
When a decision changes
The status system handles supersession. When a later discussion reverses an earlier decision, the new entry records supersedes: YYYY-MM-DD-old-slug and the old entry gets updated with status: superseded and a pointer forward.
Both entries stay in the log. The old decision still happened, and the reasoning behind it is still worth keeping. Sometimes you need to know not just what the current approach is, but that the original one was tried and why it was abandoned. The chain gives you that.
The supersede skill handles this directly: point it at a past entry and it marks the entry superseded or obsolete without requiring a new discussion session. Obsolete differs from superseded: obsolete means the decision no longer applies and there is no replacement, just a change in direction.
Installing and using it
Two commands, then restart Claude Code:
/plugin marketplace add DustinVK/wherefore
/plugin install wherefore@dustinvk
The plugin ships its own tooling. The log itself lives in each project repository, created on first use.
First-time setup
Bootstrap your tag vocabulary. Rather than inventing area and topic tags from scratch, run:
/wherefore:seed
Claude inspects your codebase, module layout, routes, dependency manifests, migrations, and proposes a starter set with a short justification for each tag. Confirm, edit, or trim the list and it writes wherefore/topics.md. Good tags are coarse and stable: checkout, billing, auth, postgres. Not price-rounding-edge-case.
Wire up the trigger. Paste the snippet from CLAUDE.snippet.md into your project's CLAUDE.md. This makes Claude offer to log a decision when a session reaches one. Capturing becomes a yes instead of something someone has to remember to initiate.
Both steps are optional. The log works without them; the trigger just becomes manual and the vocabulary grows organically.
Logging a discussion
At the end of a huddle or design conversation, paste the summary and say "log this discussion." Raw transcript, Slack export, AI-generated recap: any of these work.
Claude distills it into a compact entry: what was decided, why, and what alternatives were rejected. Not a transcript. The useful residue. It tags the entry from your controlled vocabulary, checks whether it reverses any earlier decision and links the two entries if so, and shows you the result before writing anything. You get a chance to correct the tags or title before anything is committed.
The entry lands in wherefore/log/YYYY-MM-DD-short-slug.md and a one-line summary is appended to wherefore/INDEX.md. Both files go into the repo like any other code: PR-reviewable, blame-traceable, and searchable with standard tools.
Querying the log
Ask in plain English:
- "Why did we implement the price calculator the way we did?"
- "What did we decide about PROJ-1234?"
- "How are we handling tenant isolation?"
Claude reads INDEX.md to shortlist relevant entries without opening every file, then pulls the decisions and rationale from the matching ones. If a decision was later reversed, it follows the supersession link and answers from the current entry, and tells you the decision changed so you are not acting on something that was revisited. If nothing matches, it says so plainly.
What it does not do
A few honest limitations.
It only captures what you bring to it. If your team makes a significant call in a meeting that does not involve a Claude session, that decision will not end up in the log unless someone takes time afterward to summarize it and run the skill. It is not passive. It requires a habit.
It is also not a replacement for formal decision records on things that affect the whole organization. An ADR process with review and stakeholder sign-off is the right tool for that. This covers the smaller daily calls that are not worth a formal process but are still worth more than a Slack thread that scrolls away.
Final notes
The thing I wanted most from this was the ability to ask "why does this work this way?" and get a real answer instead of a shrug or a two-hour archaeology project through old messages. That is what it does. The log is not comprehensive, but it has been useful enough to pull up more than once already, which is more than I can say for most documentation I have written.
That is all for today.
//Comments