Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,24 @@ func NewServiceClient(addr string) (*ServiceClient, error) {

A coding agent can now `grep -r "@ADR"` and surface every architectural decision site in the codebase.

### Workaround logs

Not every significant change is a decision — some are **workarounds**: monkey patches, shims, vendored patches, pins "until upstream fixes it". `analyze.py` detects these from diff content (HACK/WORKAROUND/FIXME-until markers, shim/adapter/polyfill files, `patches/`) and routes them to a different record type with the fields MADR lacks: **Trigger** (the upstream bug) and **Removal Condition** (when it can be deleted).

```
docs/decisions/
├── 0006-grpc-inter-service-transport.md ← decision (MADR)
└── 0007-wa-aws-multipart-metadata.md ← workaround log (type: workaround)
```

Workaround sites get a distinct tag prefix, so live workarounds are separately greppable:

```typescript
// @WA-0007-aws-multipart-metadata: manual metadata copy around aws-sdk bug — see docs/decisions/0007-wa-aws-multipart-metadata.md
```

The same citation verifier covers them (`marker:"<added line>"` evidence pinned to the introducing commit), and `judge.py check-workarounds` turns the log into a standing cleanup queue: it flags active records whose marker line has disappeared from the working tree ("possibly removed — update status") and, with `--check-upstream`, linked upstream issues that have since closed ("removal condition may be met").

---

## Modes
Expand Down
41 changes: 35 additions & 6 deletions SKILL.MD
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,17 @@ Throughout the rest of this skill, `docs/decisions/` refers to the **scope-resol
1. DETECT → chunking strategy (merge / squash / rebase / direct)
2. CHUNK → group commits within boundaries into logical units
2.5 CLUSTER → merge related chunks ACROSS boundaries into decision candidates
3. CLASSIFY → filter architectural decisions from noise
4. GENERATE → produce MADR per decision candidate (batch-aware)
5. TAG → place @ADR inline markers in code
3. CLASSIFY → filter architectural decisions from noise + route record type
4. GENERATE → produce MADR (decision) or workaround log per candidate
5. TAG → place @ADR / @WA inline markers in code
```

### Quick mode (default for repos under ~500 commits)

The full loop above earns its weight on large histories with many interleaved decisions. For a small/medium repo it is overkill — `analyze.py` already does the heavy lifting deterministically, so use it as a **scored classifier** and write from its output:

1. Run once: `python3 scripts/analyze.py <repo> [--code module:<path>] --json`. With no `--config`, edge specificity auto-tunes from commit count, so clustering is sane out-of-box (no mega-cluster).
2. Triage from the JSON directly — each candidate carries `score`, `classification`, and a `diff_summary` (files added/deleted/modified), so you can rank without running `git diff` by hand.
2. Triage from the JSON directly — each candidate carries `score`, `classification`, `record_type` (`decision` | `workaround`), and a `diff_summary` (files added/deleted/modified), so you can rank without running `git diff` by hand. Candidates with `record_type: "workaround"` are generated from the workaround template regardless of their score class — a borderline `fix:` commit carrying a HACK marker is exactly what the workaround log exists for.
3. Still honour the **Step 4 hard gate**: before writing each MADR, read the actual diff for that candidate (`diff_summary` is for triage, not a substitute). Write straight from the MADR template.
4. Verify with `judge.py verify-adr` / `check-tags` as the safety net.
5. Skip CHUNK/CLUSTER hand-curation and batch-generation choreography — they add no value at this scale.
Expand Down Expand Up @@ -450,6 +450,33 @@ python3 scripts/judge.py check-tags . <file1> <file2> ... --json

---

## Workaround Logs (record_type: workaround)

`analyze.py` flags candidates whose diffs carry workaround evidence — HACK/WORKAROUND/KLUDGE/XXX markers, monkeypatch/polyfill/shim wording, FIXME/TODO with until/upstream/temporary, or a workaround-shaped file (shim/adapter/polyfill, `patches/`, `*.patch`) — as `record_type: "workaround"` with the matched lines in `workaround_signals`. These get a **workaround log**, not a MADR. An architectural candidate that also carries markers stays a MADR (signal: `WORKAROUND: markers present`) — mention the workaround inside that ADR instead.

**Routing rules:**

| Aspect | Rule |
|--------|------|
| Template | `references/workaround-log-template.md` (Trigger / Workaround / Evidence / Scope / Removal Condition) |
| Output | same scope-resolved `docs/decisions/` pool, same numbering sequence |
| Filename | `NNNN-wa-<slug>.md` (the `-wa-` infix is the convention; `type:` frontmatter is the contract) |
| Frontmatter | `type: workaround`, `status: active` |
| Tag | `@WA-NNNN-<slug>: <summary> — see docs/decisions/NNNN-wa-<slug>.md` — ONE tag, at the workaround site itself |
| Verification | same `judge.py verify-adr --commits ...` call — for `type: workaround` it verifies the **Evidence** section instead of Considered Options; cite added lines with `marker:"<exact line>"` |

**The Removal Condition is the point.** A decision records *why*; a workaround records *until when*. Always extract a mechanical removal condition from the marker comment or commit message ("remove when aws-sdk >= 3.500"); if none is recoverable, write "No removal condition recorded — review periodically." Never invent one.

**Lifecycle (re-runs and CI):**

```bash
python3 scripts/judge.py check-workarounds <repo> [--check-upstream] [--json]
```

Scans all `type: workaround` records: an `active` record whose cited `marker:` line is gone from the working tree is flagged "possibly removed — update status"; with `--check-upstream` (needs `gh`), a closed upstream issue linked in the record is flagged "removal condition may be met". The tool only reports — update `status:` to `removed`/`superseded` by hand after confirming.

---

## Autonomous Mode Summary

Print at the end:
Expand All @@ -466,7 +493,8 @@ Chunks found: 23
Borderline: 4 (skipped — use assisted mode to review)
Skipped: 11
ADRs written: 8 (docs/decisions/0004–0011)
Tags placed: 19 markers across 12 files
Workaround logs: 2 (docs/decisions/0012-wa-*, 0013-wa-*)
Tags placed: 19 @ADR + 2 @WA markers across 13 files
Skipped tagging: 2 files (JSON — no comment syntax)
```

Expand All @@ -489,4 +517,5 @@ Skipped tagging: 2 files (JSON — no comment syntax)
## Reference Files

- `references/madr-examples.md` — 2 full worked examples: one with evidenced alternatives, one with none
- `references/language-tag-patterns.md` — comment syntax edge cases (decorators, JSDoc, annotation processors)
- `references/language-tag-patterns.md` — comment syntax edge cases (decorators, JSDoc, annotation processors)
- `references/workaround-log-template.md` — workaround log template, worked example, status lifecycle, @WA tagging
142 changes: 142 additions & 0 deletions references/workaround-log-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Workaround Log Template

Template for candidates with `record_type: "workaround"`. Same `docs/decisions/`
pool and numbering sequence as MADRs — only the filename infix (`-wa-`), the
frontmatter `type`, and the tag prefix (`@WA`) differ.

The load-bearing fields are the ones MADR does not have: **Trigger** (what
upstream bug/limitation forced this), and **Removal Condition** (when this can
be deleted). A workaround log without a removal condition is archaeology; with
one it is a standing cleanup queue that `judge.py check-workarounds` can track.

## Template

```markdown
---
type: workaround
status: active
---

# <short workaround title — name the thing worked around, not the commit>

> Note: This workaround log was retroactively generated from commit history
> by adr-generator. See linked commits to verify.

## Context

<What the code needed to do, and why the normal path failed. From the diff and
message — same evidence discipline as a MADR.>

## Trigger

<The upstream bug, missing feature, or constraint that forced the workaround.
Link the upstream issue/PR if the commit message or code comment records one.
If nothing is recoverable: "Trigger not explicitly recorded; inferred from diff.">

## Workaround

<What was actually done — the monkey patch, the shim, the pin, the vendored
patch. Describe mechanism, not intent.>

## Evidence

<STRICT RULE — same as MADR Considered Options: every bullet carries an inline
citation the verifier can check against git. The natural type here is `marker:`
(an ADDED line — the workaround's own comment or pin):

- <fact> <!-- evidence: <sha> marker:"<exact added line>" -->
- <fact> <!-- evidence: <sha> added:<path> --> (shim/adapter file)
- <fact> <!-- evidence: <sha> message:<phrase> --> (e.g. "until upstream fix")

If none can be cited, write exactly "No verifiable evidence recorded." —
`verify-adr` drops anything it cannot pin to the bytes.>

## Scope

<Files/modules affected; blast radius if the workaround misbehaves.>

## Removal Condition

<The single most valuable line in this record. Be mechanical and checkable:
"Delete when aws-sdk >= 3.500 (fix for aws/aws-sdk-js-v3#4332) is adopted."
If not recoverable: "No removal condition recorded — review periodically.">

## Links

- Commits: <sha1>, <sha2>
- Upstream: <issue/PR URL if recorded>
- Generated by: adr-generator [autonomous|assisted] on <date>
```

## Worked example

```markdown
---
type: workaround
status: active
---

# Copy multipart metadata manually around aws-sdk upload bug

> Note: This workaround log was retroactively generated from commit history
> by adr-generator. See linked commits to verify.

## Context

S3 multipart uploads silently dropped user metadata, so uploads completed but
objects lost their tags downstream.

## Trigger

aws-sdk drops metadata on multipart uploads (upstream bug; fix expected in the
3.5xx line). Referenced in the marker comment.

## Workaround

`upload()` copies the metadata map onto each part manually before dispatch,
duplicating what the SDK should do.

## Evidence

- HACK marker on the manual copy <!-- evidence: 81c4f02 marker:"// HACK: drop metadata copy until aws-sdk multipart fix ships upstream" -->

## Scope

`src/upload.ts` only; every multipart upload path goes through it.

## Removal Condition

Delete the manual copy when aws-sdk ships the multipart metadata fix and the
dependency is bumped past it.

## Links

- Commits: 81c4f02
- Generated by: adr-generator autonomous on 2026-06-10
```

## Status lifecycle

`status:` in the frontmatter is machine-read by `judge.py check-workarounds`:

| status | meaning |
|--------|---------|
| `active` | workaround is in the code; tracked by check-workarounds |
| `removed` | workaround deleted from code; record kept for history |
| `superseded` | replaced by a proper fix or another record — link it |

`check-workarounds` flags an `active` record whose cited marker line is no
longer in the working tree ("possibly removed — update status"), and with
`--check-upstream`, an upstream issue that has closed ("removal condition may
be met"). Update `status:` by hand after confirming — the tool never rewrites
records.

## Tagging

```
// @WA-0007-aws-multipart-metadata: manual metadata copy around aws-sdk bug — see docs/decisions/0007-wa-aws-multipart-metadata.md
```

Place ONE tag at the workaround site itself (the marker comment's enclosing
function), not at every caller. `@WA` is grep-distinct from `@ADR` so agents
can list live workarounds separately: `grep -rn "@WA-" src/`.
Loading
Loading