# Decisions Logger

> Mines a project for the architectural and process decisions that were actually made and writes each as a numbered ADR into docs/decisions/ — with evidence citations, an index table plus supersession graph, and append-only follow-ups. Proposes candidates with their sources and asks when the "why" was never written down; it never invents one. Use when the user says "log this decision", "record why…

- **Type:** Skill
- **Install:** `agentstack add skill-vidhunnan-agentic-skills-decisions-logger`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [vidhunnan](https://agentstack.voostack.com/s/vidhunnan)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [vidhunnan](https://github.com/vidhunnan)
- **Source:** https://github.com/vidhunnan/agentic-skills/tree/prod-stable/skills/decisions-logger

## Install

```sh
agentstack add skill-vidhunnan-agentic-skills-decisions-logger
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# decisions-logger

Fills the **decisions tier** — the folder that answers *"why did we choose that?"*. It mines a project for choices that were really made, and writes each as a numbered ADR with the evidence it came from.

Decisions are the **truth tier**: past tense, append-only, superseded rather than edited. Which makes this skill's one real risk the whole design problem: **mining a repo for "why we chose X" is exactly where a model invents rationale that sounds right.** A plausible fabricated reason is indistinguishable from a real one to every future reader, and it poisons the tier the rest of the project trusts.

So the guiding rule, inherited from `changelog-tracker`, is **faithful, not generative**: every clause traces to a source or to the user's own words.

Two principles do the work:

- **A candidate may be *born* in a weak source. It may never be *justified* by one.** Some files state a rule and never state its reason (`branch names carry no area segment`). You must be able to *find* those decisions — so you read the file — but their prose can never reach an ADR's reasoning fields. It is a firewall, not a ban.
- **"I don't remember" is always an option.** Every interview question offers it, and it produces a real ADR with the reason recorded as `*(reason not stated)*`. **A decision with an honest gap is worth more than one with a plausible fiction.**

## Instructions

### Step 0 — Detect your surface

Decide where you're running, using **Bash availability**:

- **Claude Code** — Bash works, real filesystem and git. Full flow.
- **Claude.ai** — no filesystem, no git, no existing ADRs to compare against. Degrade: ask the user to paste their decisions index (or say there isn't one), run the interview conversationally, and emit each ADR as a downloadable artifact plus the index region and the CLAUDE.md block to paste. **Say plainly that dedup, supersession detection, and the shipping cross-check are unavailable here — do not guess at them.**

Confirm the repo: `git rev-parse --show-toplevel`. If it fails, the git-anchored sources are gone; say so and degrade (see Step 15).

**Argument modes:**

- *(no argument)* — the default: **everything not yet logged.** Steps 1–14.
- `check` — Steps 1–6 plus a report, with **zero writes**. A lint you can run any time.
- `reconsider` — the default flow, but **ignore the reject ledger for one run**, re-opening everything previously declined or filtered out.
- `""` — the **fast path** (Step 3B). The user just made a decision and is naming it. Skip the mine; interview and write one ADR.

### Step 1 — Resolve the decisions path

Reuse `repo-setup`'s resolution order, in descending authority:

1. **A path declared in a CLAUDE.md protocol block** — `skill:repo-setup`'s routing table row for Decisions, or this skill's own block. A declared path beats everything.
2. **An existing folder on disk** — `docs/decisions/`, `docs/adr/`, `adr/`, `architecture/decisions/`, a root `DECISIONS.md`. Match **case-insensitively** (macOS silently reuses `docs/Decisions`).
3. **Canon** — `docs/decisions/`.

If **nothing exists**: do not create it silently. Say the tier is missing and recommend `/repo-setup add decisions` — **`repo-setup` owns the tier's README and `_TEMPLATE.md`**, and authoring them here would be the stale-copy failure that skill explicitly forbids. If the user declines, offer to create just the folder and the ADRs.

Detect the **existing numbering scheme** (`0001-`, `001-`, `ADR-001-`) and **adopt it. Never renumber** — renumbering breaks every inbound link and every supersession edge.

### Step 2 — Load the existing record (the idempotency baseline)

Before mining anything, read:

1. **Every ADR** in the tier — number, title, date, status, `Supersedes`, the `## Evidence` citations, and the normalized claim (below).
2. **`{decisions}/0000-not-logged.md`** — the reject ledger (Step 11). This is what stops rejected candidates resurfacing forever.
3. **The index region** (``), if present.

**The normalized claim** is the unit of comparison. Reduce every ADR and every candidate to:

```
{subject} := {chosen option}  ⟂  {rejected option}
```

e.g. `packaging-of-a-skill := folder-carries-its-own-plugin.json ⟂ separate-plugins-tree`

**Dedup and supersession are the same comparison with two outcomes** — build it once:

| Candidate vs. a logged ADR | Outcome |
|---|---|
| same subject, **same** chosen option | **duplicate** → drop |
| same subject, **different** chosen option | **supersession candidate** → Step 9. Never a new standalone ADR. |
| different subject | **new** → proceed |

Cheap pre-pass first: **evidence-anchor overlap.** If a candidate's primary anchor (`path#heading`, `git:hash`) already appears in a logged ADR's Evidence block, presume it's logged; escalate to the semantic comparison only if the claim looks different.

### Step 3 — Mine the sources

#### 3A — The default path (full backfill)

**The tier governs what a source is allowed to contribute.** This table is the most important thing in the skill:

| Tier | Sources | May supply | May **never** supply |
|---|---|---|---|
| **S — primary** | `docs/MODEL-STRATEGY.md` (often has a literal *Why* column) · `handoff/*.md` → `## Decisions Made` (already shaped `{decision} — {why}`, alternatives often named inline) · the tier `README.md`s (argued prose) · **CLAUDE.md narrative prose** — the sections *outside* the protocol markers | claim, why, alternatives, date | — |
| **A — corroborating** | PRD `§Risks`, `§Non-goals`, rationale paragraphs | the *why*, the named alternative | **existence.** A PRD may never be the `Primary:` citation. |
| **B — anchors** | `git log` bodies (non-merge, multi-line) | date, hash, purpose clause, **proof it shipped** | the rejected alternative. Never the sole rationale. |
| **C — index/dating** | `changelog/CHANGELOG.md`, `changelog/commits/*.md` | date, hash, shipping proof | **any rationale.** These are summaries — follow them back. |
| **D — rules, not reasons** | CLAUDE.md `` blocks · `README.md` conventions list · `CONTRIBUTING.md` · `.github/` | **the rule only** — i.e. the fact that a fork happened | **any word** of Context, Alternatives, or Consequences. Firewalled. |
| **X — never mine** | PRD `§Open questions for v2` (unresolved ≠ decided) · `{decisions}/` itself | — | everything |

Three invariants follow, one per failure mode:

**Invariant 1 — No confabulation.** Every sentence in Context / Alternatives / Consequences traces to Tier S, A, B, or a user answer. **Tier C and D prose may never be paraphrased into those sections.** A candidate whose *only* source is Tier D is stamped `WHY: NOT STATED` **by construction** and routed to the interview — it cannot be written from mined text at all.

> Why this matters: a block that says *"Branches follow `/`. **No area segment.**"* reads exactly like a decision and contains **zero reasoning**. A model that reads it will produce a beautiful, confident, entirely fabricated Context paragraph. The ban has to be on the *destination fields*, not on reading the file — because you must read it to find the candidate at all.

**Invariant 2 — Proposal ≠ truth.** An ADR requires **shipping evidence**: a Tier B commit, a Tier C changelog entry, or the artifact existing on disk. **A PRD alone is never enough** — a PRD describes what *should* be true.

**Invariant 3 — Exactly one Primary, ranked by originality, not recency.** If two sources carry the same claim, the primary is **the one the other is describing.** A changelog entry that summarizes `docs/MODEL-STRATEGY.md` is *more recent* and is **not** the source — follow the link and cite the file, not the summary of it. Recency is precisely the wrong tiebreak.

#### 3B — The fast path (`/decisions-logger ""`)

No mining. The evidence is **this session**: the diff, the conversation, the user's own words. Confirm the claim, ask for the loser and the cost, **still run the Step 2 dedup** (a milestone is the *most* likely moment to re-log something already in the folder), write one ADR, rebuild the index.

Offer this **before staging the commit**, so the ADR lands alongside the change it explains.

### Step 4 — The significance filter, and granularity

#### The significance filter — two gates

**Gate 1 — The Fork Test (hard gate). Name the loser.**

Can you name a *specific alternative that a reasonable person would have chosen*? If not, this is a **fact, a task, or a chore** — not a decision. This gate is not overridable by anyone's enthusiasm.

- *"Added a `.gitignore` for OS cruft"* → the loser is "commit `.DS_Store`". Nobody would defend it. **Fail.**
- *"Skill folder is also the plugin"* → the loser is a separate `plugins/` tree. Real, defensible, common. **Pass.**

**Gate 2 — Blast radius.** Reversal must touch **more than one file, or force a second decision.** Removing a `.gitignore` line is one edit. Reversing `handoff/` → `docs/handoffs/` after files exist means a rename, a README note, a CLAUDE.md block, and a skill's write path. That's the line.

**Two collapse rules that remove most of the noise:**

- **Policy, not instance.** A source line recording *compliance* with a rule is not a decision — **the rule is.** *"Committed on `feat/x`, not directly on `prod-stable`"* is an instance; the ADR is the policy (*"`prod-stable` is the protected default branch"*), whose reason may well not be stated anywhere. Collapse instances upward.
- **Fact, not choice.** *"This project makes no model calls of its own"* is a fact about the repo, not a fork. Drop.

Everything filtered out is written to the reject ledger as `below-bar` — **that is what makes the filter idempotent** instead of re-running its own judgment every time.

#### Granularity — one fork, one ADR

**The test is independent reversal:** could you reverse rule A without re-opening rule B? If reversing *"Sonnet for plumbing"* forces you to re-examine *"Opus is the default"*, they are **one decision**.

Two mechanical corollaries:

- If two candidates would share the same **Context** paragraph, or the same **Alternatives** list → one ADR.
- If the title needs an **"and"**, or is a comma-list of four things → you have more than one ADR, *or* you haven't found the principle. Name **the principle**, not the list.

So a model-strategy document yielding *"Opus default / Sonnet plumbing / Fable break-glass / Haiku never"* is **one ADR, not four** — those are four rows of a single routing policy, sharing one Context and one rationale spine. Its mandatory-review rule, however, is a **separate** ADR: independently reversible, its own stated why.

**Anti-rule: co-location is not co-reasoning.** Never merge two decisions because they landed in the same commit or the same document.

### Step 5 — Dedup against the record

Run the Step 2 comparison against **both** ledgers. Against `0000-not-logged.md`, by class:

| Class | Re-proposed? |
|---|---|
| `below-bar` — the filter rejected it | **Never** (unless `reconsider`) |
| `declined` — the user said no | **Never** (unless `reconsider`, or the user deletes the row) |
| `deferred` — the user said *not now* | **Yes**, but demoted to a footnote, never the main table |
| `unshipped` — a proposal with no shipping evidence | **Automatically, once shipping evidence appears.** Invariant 2 clears itself. |

The `declined` / `deferred` split is the humane part: **"no" is permanent; "not now" is a snooze.** That is what stops rejected candidates reappearing on every run forever.

**Always print the suppression count**, even when nothing was suppressed:

```
Previously declined (not re-proposed): 3 — see 0000-not-logged.md.
Run /decisions-logger reconsider to re-open them.
```

Suppression the user can't see is indistinguishable from a bug.

### Step 6 — Present the candidates (the propose gate)

**Nothing is written before this.** One ranked table — claim (with its loser), primary evidence, and a why-verdict on every row:

```
Decision candidates — 22 found · 18 with a stated "why" · 4 without

| #  | Decision (the claim, with its loser)                              | Primary evidence                          | Why?                   |
|----|-------------------------------------------------------------------|-------------------------------------------|------------------------|
| 1  | Each skill folder is also a plugin — over a separate plugins/ tree | handoff/…-build-publish.md §Decisions Made| STATED                 |
| 2  | Enforce conventions via CLAUDE.md blocks, not git hooks            | CLAUDE.md §Skill protocols (narrative)     | STATED                 |
| …  |                                                                   |                                           |                        |
| 19 | Branch names carry no area segment — over feat//       | CLAUDE.md skill:branch-naming (Tier D)     | NOT STATED — will ask  |

Below the bar (recorded in 0000-not-logged.md, not proposed again):
  • Added a .gitignore for OS cruft — no loser (Fork Test)
  • "Committed on feat/… not prod-stable" — an instance; collapsed into #19's sibling

Previously declined (not re-proposed): 0
```

Then **one** AskUserQuestion: **Write all with a stated why** / **Select individually** / **Cancel**. **Do not fire twenty AskUserQuestions.** Reason-less candidates are never in this batch — they go to Step 7 by construction.

### Step 7 — Interview the gaps (never invent a why)

Only for `NOT STATED` rows. Cap at **4 questions per round.** For each, show the rule, the loser you inferred, and ask:

> `skill:branch-naming` says branches are `/` with **no area segment**. The alternative was `feat//`. The reason isn't written anywhere I can find. Why no area segment?
>
> **(a)** I'll tell you · **(b)** I don't remember — log it as *(reason not stated)* · **(c)** Skip for now · **(d)** Don't log this

Three honest outcomes:

- **(a)** → `**Rationale:** supplied by {user} on {date} (not written down at the time)`. **That provenance line matters** — a reason reconstructed years later is not the same artifact as one recorded in the moment, and the record should say so.
- **(b)** → write the ADR with the alternative **named** and the reason literally `*(reason not stated)*`. Context describes **what was true**, never a motive. **This is a legitimate and valuable ADR** — *"we did this, nobody wrote down why"* is exactly what a reader needs to know before they change it.
- **(c)/(d)** → the reject ledger, class `deferred` / `declined`.

**Hard rule: option (b) must appear on every question.** The moment "I don't know" isn't offered, the model fills the gap. This is the single most important choice in the skill.

### Step 8 — Write the ADRs

- **Numbering:** 4-digit, zero-padded, from `0001`. **`0000` is reserved** for the reject ledger, which also keeps it out of the sequence.
- Next = highest existing prefix + 1. **Re-scan immediately before each write, bump on collision, never overwrite.**
- **Never reuse a burned number**, even if an ADR file was deleted. A burned number is cheaper than a broken link.
- **Slug:** kebab-case of the decision statement, ≤ 60 chars → `0002-enforce-conventions-in-claude-md-not-git-hooks.md`.
- **Date = when the decision was MADE**, taken from the evidence (the handoff's date, the commit's date) — **not today.** If only the earliest evidence date is available, use it and mark `(approx.)`. Today's date appears only in Follow-up entries and the reject ledger. *Backfilling with `date +%F` destroys the chronology the supersession graph depends on.*
- If the folder has a **pre-existing template** with different sections, adopt it. Never rewrite existing ADRs to match a new one.

### Step 9 — Supersession

**The rule, stated precisely:** append-only applies to the *reasoning*, not the *pointers*. The frozen region is **Context / Decision / Alternatives / Consequences** — the record of what was thought at the time. The **Status line is navigational**: it says where this file sits relative to th

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [vidhunnan](https://github.com/vidhunnan)
- **Source:** [vidhunnan/agentic-skills](https://github.com/vidhunnan/agentic-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-vidhunnan-agentic-skills-decisions-logger
- Seller: https://agentstack.voostack.com/s/vidhunnan
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
