AgentStack
SKILL verified MIT Self-run

Decisions Logger

skill-vidhunnan-agentic-skills-decisions-logger · by vidhunnan

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…

No reviews yet
0 installs
1 views
0.0% view→install

Install

$ agentstack add skill-vidhunnan-agentic-skills-decisions-logger

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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.

Are you the author of Decisions Logger? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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 blockskill:repo-setup's routing table row for Decisions, or this skill's own block. A declared path beats everything.
  2. An existing folder on diskdocs/decisions/, docs/adr/, adr/, architecture/decisions/, a root DECISIONS.md. Match case-insensitively (macOS silently reuses docs/Decisions).
  3. Canondocs/decisions/.

If nothing exists: do not create it silently. Say the tier is missing and recommend /repo-setup add decisionsrepo-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.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 .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-barthat 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.

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

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.