Install
$ agentstack add skill-vidhunnan-agentic-skills-decisions-logger ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
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:
- 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. - An existing folder on disk —
docs/decisions/,docs/adr/,adr/,architecture/decisions/, a rootDECISIONS.md. Match case-insensitively (macOS silently reusesdocs/Decisions). - 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:
- Every ADR in the tier — number, title, date, status,
Supersedes, the## Evidencecitations, and the normalized claim (below). {decisions}/0000-not-logged.md— the reject ledger (Step 11). This is what stops rejected candidates resurfacing forever.- 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.mds (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
.gitignorefor 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 onprod-stable" is an instance; the ADR is the policy ("prod-stableis 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.0000is 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 withdate +%Fdestroys 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
- Source: vidhunnan/agentic-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.