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
14 changes: 11 additions & 3 deletions .agents/sessions/20260502-184737-session.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
branch: refactor/tighten-architecture-skill
branch: main
status: active
created: '2026-05-02T17:47:37.534Z'
last_entry: '2026-05-02T20:18:22.047Z'
last_entry: '2026-05-02T22:24:22.089Z'
claude_session_id: c25bffc1-3fe6-44c5-bdbd-5448cb2941c8
last_updated: '2026-05-02T20:18:22.047Z'
last_updated: '2026-05-02T22:24:22.089Z'
---
# Session: Ad-hoc

Expand Down Expand Up @@ -33,3 +33,11 @@ last_updated: '2026-05-02T20:18:22.047Z'
[2026-05-02 21:08] skill(release): orchestrating release for refactor/tighten-architecture-skill — 6 commits, 2 review rounds, ready for PR + version bump
[2026-05-02 21:18] decision(release): bump 2.0.0-dev.37 → 2.0.0-dev.38 (prerelease). Curated changelog approved with revision — drop internal/session terms (Q1/Q2/Q3, gate logic notation).
[2026-05-02 21:18] spark(git-workflow): add changelog-discipline guideline to git-workflow skill — no internal/session terms in user-facing changelogs (specs, tasks, internal Q1/Q2 notation, gate-logic shorthand). User-perspective prose only.
[2026-05-02 22:53] decision(release): v2.0.0-dev.38 shipped via PR #46 — squash commit c51785d4, tag pushed, GH Release published, branches cleaned. Manual fallback used because loaf release --post-merge guardrail 3 expects 'chore: release vX.Y.Z' subject but our convention is squash-subjects-reflect-the-work (refactor:). Known issue from idea note e1aa9f37.
[2026-05-02 23:16] decision(architecture): extend ADR lifecycle vocabulary — add Rejected status. Five statuses: Proposed | Accepted | Rejected | Deprecated | Superseded. Body section required for Rejected and Deprecated; optional for Superseded (linkage carries it).
[2026-05-02 23:16] decision(architecture): finalize ADR frontmatter schema — keep status, date, accepted_date (optional), rejected_date, deprecated_date, supersedes, superseded_by. Drop deprecated_reason and migrated_to (move to ## Deprecated body where they have prose context). Frontmatter is structured what+when; body section is contextual why+where-it-went.
[2026-05-02 23:16] decision(reflect): proposals approved — ARCHITECTURE.md gets Adversarial Review operating principle (codex-optional framing) + recategorization-as-general-pattern note. No STRATEGY.md or VISION.md changes (tactical refinement, not strategic shift).
[2026-05-02 23:21] commit(49d1e25f): refactor: extend ADR lifecycle (Rejected status, body-section rules, frontmatter cleanup)
[2026-05-02 23:21] commit(79f1f119): chore: build update bundled CLI
[2026-05-02 23:21] commit(711be775): chore: post-merge bookkeeping
[2026-05-02 23:24] decision(release): push 3 follow-up commits direct to main — single refactor commit reads like a squash-merge result; small post-PR-46 follow-up doesn't warrant separate PR cycle.
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
},
"metadata": {
"description": "Loaf - An Opinionated Agentic Framework",
"version": "2.0.0-dev.38"
"version": "2.0.0-dev.39"
},
"plugins": [
{
"name": "loaf",
"description": "Loaf - An Opinionated Agentic Framework",
"source": "./plugins/loaf",
"version": "2.0.0-dev.38",
"version": "2.0.0-dev.39",
"license": "MIT",
"repository": "https://github.com/levifig/loaf"
}
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- _No unreleased changes yet._

## [2.0.0-dev.39] - 2026-05-02

### Added

- ADR lifecycle now supports `Rejected` as a fifth status. Full lifecycle: `Proposed | Accepted | Rejected | Deprecated | Superseded`. A `Rejected` ADR records "the team weighed this option and explicitly chose against it" — useful when the same idea resurfaces.

### Changed

- `architecture` skill — Lifecycle section codifies body-section requirements by status. `## Deprecated` is required for `Deprecated`, `## Rejected` is required for `Rejected`, `## Superseded` is optional for `Superseded` (the `superseded_by:` linkage suffices).
- ADR frontmatter schema finalized as structured what+when: `status`, `date`, `accepted_date` (optional), `rejected_date`, `deprecated_date`, `supersedes`, `superseded_by`. The `deprecated_reason` and `migrated_to` fields introduced during the previous deprecation pass are dropped — context belongs in the body section's prose, not duplicated in frontmatter.
- ADR template (`content/templates/adr.md`) updated with the new schema and a header note that `Rejected` and `Deprecated` ADRs require a body section.
- `ADR-004`, `ADR-006`, `ADR-009` frontmatter cleaned up to match the new schema; body sections preserve all migration content.
- `docs/ARCHITECTURE.md` Operating Principles section gains two new subsections:
- **Adversarial Review for Substantive Guidance Changes** — `loaf:reviewer` is the baseline (internal-consistency auditor); `codex:rescue` or equivalent adversarial reviewer is recommended when available, since the two readers catch different defect classes. Codex is plugin-dependent and optional.
- **Recategorization as a General Lifecycle Pattern** — distinguishes supersession (the answer changed; new artifact replaces old) from recategorization (the artifact's classification was wrong; the underlying rule still holds; deprecate-in-place and point to new home). Generalizes beyond ADRs.

## [2.0.0-dev.38] - 2026-05-02

### Changed
Expand Down
38 changes: 33 additions & 5 deletions content/skills/architecture/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,42 @@ Q1 and Q2 form a disjunction (matches Microsoft's bar — either canonical-domai

ADRs are **append-only post-acceptance**. The original `Decision`, `Context`, `Rationale`, and `Consequences` sections are immutable — don't rewrite history.

Five statuses: `Proposed` | `Accepted` | `Rejected` | `Deprecated` | `Superseded`.

What's permitted post-acceptance:
- **Status transitions:** `Accepted → Deprecated`, `Accepted → Superseded`
- **Frontmatter additions:** `superseded_by`, `deprecated_date`, `deprecated_reason`, `migrated_to`
- **Append-only `## Deprecated` or `## Superseded` sections** capturing the lifecycle change
- **Status transitions:** `Accepted → Deprecated`, `Accepted → Superseded`, `Proposed → Rejected`, `Proposed → Accepted`
- **Frontmatter additions** per the schema below (transition dates, supersession linkage)
- **Append-only `## Deprecated`, `## Rejected`, or `## Superseded` sections** capturing the lifecycle change

**Frontmatter schema:**

```yaml
---
id: ADR-NNN
title: "..."
status: Proposed | Accepted | Rejected | Deprecated | Superseded
date: YYYY-MM-DD # creation / proposal
accepted_date: YYYY-MM-DD # optional — only if differs from `date`
rejected_date: YYYY-MM-DD # required iff status is Rejected
deprecated_date: YYYY-MM-DD # required iff status is Deprecated
supersedes: ADR-NNN # optional — points back to the ADR this replaces
superseded_by: ADR-NNN # required iff status is Superseded
---
```

Frontmatter encodes the structured *what* and *when* of a status transition. Context for *why* and *where-it-went* belongs in the body section (`## Deprecated` / `## Rejected`). Don't duplicate `reason` or `migrated_to` as frontmatter fields — they're prose.

**Body-section requirements by status:**

- `## Deprecated` is **required** when status is `Deprecated`. Explains why and points to the new home (if migrated).
- `## Rejected` is **required** when status is `Rejected`. Explains why the proposal was rejected and what was chosen instead (if anything).
- `## Superseded` is **optional** when status is `Superseded`. The `superseded_by:` linkage carries the structural relationship; the new ADR carries the rationale. Add a body section only if the supersession had specific reasons worth preserving on the old record.

A `Rejected` ADR is a record of "the team weighed this option and explicitly chose against it" — useful when the same idea resurfaces. Keep them.

When the team's answer to a decision changes, write a **new ADR that supersedes the old one** — set `supersedes: ADR-NNN` on the new one and `superseded_by: ADR-MMM` on the old one. Both stay in `docs/decisions/`. The old one preserves the historical "_was_ the decision, _no longer_ the decision" record (Nygard).

When a record is **recategorized** (the underlying choice still holds, but the artifact-classification was wrong — e.g., it was actually a principle, convention, or workflow lore), mark it `Deprecated` with a `migrated_to:` field pointing to the new home. The original record is preserved; the active source is the migrated content. This is distinct from supersession: nothing's been *replaced*; only the *classification* changed.
When a record is **recategorized** (the underlying choice still holds, but the artifact-classification was wrong — e.g., it was actually a principle, convention, or workflow lore), mark it `Deprecated` and explain the migration target in the `## Deprecated` body section. The original record is preserved; the active source is the migrated content. This is distinct from supersession: nothing's been *replaced*; only the *classification* changed.

Supersession is healthy. The bar for *writing* an ADR is high; once written, the bar for *quietly diverging from* it is also high (write a superseding ADR instead).

Expand All @@ -104,7 +132,7 @@ Supersession is healthy. The bar for *writing* an ADR is high; once written, the
- Create ADRs without user approval, even when the user requests one — if the decision fails the Triage Gate, propose the correct destination and decline the ADR
- Use the word "irreversible" — software decisions can always be reversed via supersession; the operative criterion is "difficult to reverse"
- ADR-ify aesthetic preferences, naming conventions, workflow lore, or guiding principles — those have other homes (see Skip ADR When)
- Rewrite accepted ADRs' Decision/Context/Rationale/Consequences sections — those are immutable. Status transitions, frontmatter additions, and append-only Deprecated/Superseded sections are how lifecycle is recorded
- Rewrite accepted ADRs' Decision/Context/Rationale/Consequences sections — those are immutable. Status transitions, frontmatter additions, and append-only Deprecated/Rejected/Superseded sections are how lifecycle is recorded
- Block ADR creation on glossary state — glossary mutations are additive and opt-in
- Call `loaf kb glossary propose` (reserved for upstream ambiguity-resolving skills)

Expand Down
13 changes: 10 additions & 3 deletions content/templates/adr.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ If it's workflow lore for a specific skill, document it in that skill.
If it's a local choice changeable in a single PR, session-log it.

Run the architecture skill's Triage Gate if unsure.

A `Rejected` or `Deprecated` ADR REQUIRES a `## Rejected` or `## Deprecated`
body section explaining the transition. A `Superseded` ADR may include a
`## Superseded` section but the linkage in `superseded_by:` is sufficient.
-->

# ADR Template
Expand All @@ -24,10 +28,13 @@ Run the architecture skill's Triage Gate if unsure.
---
id: ADR-001
title: "PostgreSQL as Primary Database"
status: Accepted # Proposed | Accepted | Deprecated | Superseded
status: Accepted # Proposed | Accepted | Rejected | Deprecated | Superseded
date: 2026-01-23
supersedes: null # ADR-000 if replacing
superseded_by: null # ADR-002 if replaced
# accepted_date: # optional — only if differs from `date`
# rejected_date: # required iff status is Rejected
# deprecated_date: # required iff status is Deprecated
supersedes: null # optional — ADR-NNN if replacing
superseded_by: null # required iff status is Superseded
---

# ADR-001: PostgreSQL as Primary Database
Expand Down
2 changes: 1 addition & 1 deletion dist/amp/plugins/loaf.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* Amp Plugin - Agent Skills Hooks
* Auto-generated by loaf build system
* @version 2.0.0-dev.38
* @version 2.0.0-dev.39
* @experimental This plugin uses the experimental Amp plugin API
*/

Expand Down
40 changes: 34 additions & 6 deletions dist/amp/skills/architecture/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ description: >-
owning skill), or local choices changeable in a single PR (session-log
decision() instead). The ADR log is append-only — when circumstances change,
write a new ADR that supersedes the old one.
version: 2.0.0-dev.38
version: 2.0.0-dev.39
---

# Architecture
Expand Down Expand Up @@ -75,14 +75,42 @@ Q1 and Q2 form a disjunction (matches Microsoft's bar — either canonical-domai

ADRs are **append-only post-acceptance**. The original `Decision`, `Context`, `Rationale`, and `Consequences` sections are immutable — don't rewrite history.

Five statuses: `Proposed` | `Accepted` | `Rejected` | `Deprecated` | `Superseded`.

What's permitted post-acceptance:
- **Status transitions:** `Accepted → Deprecated`, `Accepted → Superseded`
- **Frontmatter additions:** `superseded_by`, `deprecated_date`, `deprecated_reason`, `migrated_to`
- **Append-only `## Deprecated` or `## Superseded` sections** capturing the lifecycle change
- **Status transitions:** `Accepted → Deprecated`, `Accepted → Superseded`, `Proposed → Rejected`, `Proposed → Accepted`
- **Frontmatter additions** per the schema below (transition dates, supersession linkage)
- **Append-only `## Deprecated`, `## Rejected`, or `## Superseded` sections** capturing the lifecycle change

**Frontmatter schema:**

```yaml
---
id: ADR-NNN
title: "..."
status: Proposed | Accepted | Rejected | Deprecated | Superseded
date: YYYY-MM-DD # creation / proposal
accepted_date: YYYY-MM-DD # optional — only if differs from `date`
rejected_date: YYYY-MM-DD # required iff status is Rejected
deprecated_date: YYYY-MM-DD # required iff status is Deprecated
supersedes: ADR-NNN # optional — points back to the ADR this replaces
superseded_by: ADR-NNN # required iff status is Superseded
---
```

Frontmatter encodes the structured *what* and *when* of a status transition. Context for *why* and *where-it-went* belongs in the body section (`## Deprecated` / `## Rejected`). Don't duplicate `reason` or `migrated_to` as frontmatter fields — they're prose.

**Body-section requirements by status:**

- `## Deprecated` is **required** when status is `Deprecated`. Explains why and points to the new home (if migrated).
- `## Rejected` is **required** when status is `Rejected`. Explains why the proposal was rejected and what was chosen instead (if anything).
- `## Superseded` is **optional** when status is `Superseded`. The `superseded_by:` linkage carries the structural relationship; the new ADR carries the rationale. Add a body section only if the supersession had specific reasons worth preserving on the old record.

A `Rejected` ADR is a record of "the team weighed this option and explicitly chose against it" — useful when the same idea resurfaces. Keep them.

When the team's answer to a decision changes, write a **new ADR that supersedes the old one** — set `supersedes: ADR-NNN` on the new one and `superseded_by: ADR-MMM` on the old one. Both stay in `docs/decisions/`. The old one preserves the historical "_was_ the decision, _no longer_ the decision" record (Nygard).

When a record is **recategorized** (the underlying choice still holds, but the artifact-classification was wrong — e.g., it was actually a principle, convention, or workflow lore), mark it `Deprecated` with a `migrated_to:` field pointing to the new home. The original record is preserved; the active source is the migrated content. This is distinct from supersession: nothing's been *replaced*; only the *classification* changed.
When a record is **recategorized** (the underlying choice still holds, but the artifact-classification was wrong — e.g., it was actually a principle, convention, or workflow lore), mark it `Deprecated` and explain the migration target in the `## Deprecated` body section. The original record is preserved; the active source is the migrated content. This is distinct from supersession: nothing's been *replaced*; only the *classification* changed.

Supersession is healthy. The bar for *writing* an ADR is high; once written, the bar for *quietly diverging from* it is also high (write a superseding ADR instead).

Expand All @@ -104,7 +132,7 @@ Supersession is healthy. The bar for *writing* an ADR is high; once written, the
- Create ADRs without user approval, even when the user requests one — if the decision fails the Triage Gate, propose the correct destination and decline the ADR
- Use the word "irreversible" — software decisions can always be reversed via supersession; the operative criterion is "difficult to reverse"
- ADR-ify aesthetic preferences, naming conventions, workflow lore, or guiding principles — those have other homes (see Skip ADR When)
- Rewrite accepted ADRs' Decision/Context/Rationale/Consequences sections — those are immutable. Status transitions, frontmatter additions, and append-only Deprecated/Superseded sections are how lifecycle is recorded
- Rewrite accepted ADRs' Decision/Context/Rationale/Consequences sections — those are immutable. Status transitions, frontmatter additions, and append-only Deprecated/Rejected/Superseded sections are how lifecycle is recorded
- Block ADR creation on glossary state — glossary mutations are additive and opt-in
- Call `loaf kb glossary propose` (reserved for upstream ambiguity-resolving skills)

Expand Down
13 changes: 10 additions & 3 deletions dist/amp/skills/architecture/templates/adr.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ If it's workflow lore for a specific skill, document it in that skill.
If it's a local choice changeable in a single PR, session-log it.

Run the architecture skill's Triage Gate if unsure.

A `Rejected` or `Deprecated` ADR REQUIRES a `## Rejected` or `## Deprecated`
body section explaining the transition. A `Superseded` ADR may include a
`## Superseded` section but the linkage in `superseded_by:` is sufficient.
-->

# ADR Template
Expand All @@ -24,10 +28,13 @@ Run the architecture skill's Triage Gate if unsure.
---
id: ADR-001
title: "PostgreSQL as Primary Database"
status: Accepted # Proposed | Accepted | Deprecated | Superseded
status: Accepted # Proposed | Accepted | Rejected | Deprecated | Superseded
date: 2026-01-23
supersedes: null # ADR-000 if replacing
superseded_by: null # ADR-002 if replaced
# accepted_date: # optional — only if differs from `date`
# rejected_date: # required iff status is Rejected
# deprecated_date: # required iff status is Deprecated
supersedes: null # optional — ADR-NNN if replacing
superseded_by: null # required iff status is Superseded
---

# ADR-001: PostgreSQL as Primary Database
Expand Down
2 changes: 1 addition & 1 deletion dist/amp/skills/bootstrap/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description: >-
I start a new project?", "set up Loaf," or "bootstrap my project." Produces
populated project documents and setup recommendations. Not for shaping
features (use shape) or brainstorming ideas (use brainstorm).
version: 2.0.0-dev.38
version: 2.0.0-dev.39
---

# Bootstrap
Expand Down
2 changes: 1 addition & 1 deletion dist/amp/skills/brainstorm/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: >-
analysis. Use when the user asks "help me think through this," "what are the
options," or is exploring tradeoffs. Produces docs with sparks. Not for quick
ideas or shaping.
version: 2.0.0-dev.38
version: 2.0.0-dev.39
---

# Brainstorm
Expand Down
2 changes: 1 addition & 1 deletion dist/amp/skills/breakdown/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: >-
Use when the user asks "break this down" or "create tasks for this spec."
Produces task files with estimates, dependencies, and acceptance criteria. Not
for shaping ideas (use shape) or implementation work (use implement).
version: 2.0.0-dev.38
version: 2.0.0-dev.39
---

# Breakdown
Expand Down
2 changes: 1 addition & 1 deletion dist/amp/skills/cli-reference/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: >-
/implement, /implement, and all loaf subcommands. Use when you need to know
which CLI command to invoke. Not for skill documentation (use the skill's own
SKILL.md) or for understanding build internals.
version: 2.0.0-dev.38
version: 2.0.0-dev.39
---

# Loaf CLI Reference
Expand Down
2 changes: 1 addition & 1 deletion dist/amp/skills/council/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: >-
the user wants a structured debate between domain-specific viewpoints. Not for
single-perspective research (use research) or architectural decisions that
don't need multi-agent deliberation (use architecture).
version: 2.0.0-dev.38
version: 2.0.0-dev.39
---

# Council
Expand Down
2 changes: 1 addition & 1 deletion dist/amp/skills/database-design/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: >-
database administration and development. Not for ORM usage in application code
(use language-specific development skills) or infrastructure orchestration
(use infrastructure-management).
version: 2.0.0-dev.38
version: 2.0.0-dev.39
---

# Database Skill
Expand Down
Loading
Loading