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
62 changes: 60 additions & 2 deletions docs/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -1100,12 +1100,13 @@ radon cc src/ -s -nb

**Structured Logging** (`structured_logging`, 1%) — JSON logs with consistent fields
**OpenAPI/Swagger Specs** (`openapi_specs`, 2%) — Machine-readable API docs
**Progressive Disclosure** (`progressive_disclosure`, 2%) — Path-scoped rules, skills for focused context (moved from T4)
**Progressive Disclosure** (`progressive_disclosure`, 1%) — Path-scoped rules, skills for focused context (moved from T4)
**ADR Frontmatter Completeness** (`adr_frontmatter_completeness`, 2%) — Structured YAML frontmatter in ADR files enabling automated filtering by status and applicability
### Architecture Decision Records

**ID**: `architecture_decisions`
**Tier**: Tier 3
**Weight**: 2%
**Weight**: 1%
**Category**: Documentation Standards
**Status**: ✅ Implemented

Expand Down Expand Up @@ -1166,6 +1167,63 @@ EOF

---

### ADR Frontmatter Completeness

**ID**: `adr_frontmatter_completeness`
**Tier**: Tier 3
**Weight**: 2%
**Category**: Documentation Standards
**Status**: ✅ Implemented

#### Definition

Scores repositories on whether ADR files contain structured YAML frontmatter with the required `status` and `applies_to` fields. Machine-readable frontmatter enables automated tooling (and AI agents) to filter ADRs by lifecycle state and applicability to a given service.

#### Why It Matters

ADRs without frontmatter are human-readable only. With `status` and `applies_to`, agents can determine whether a decision is still active and whether it applies to the repository they are working in — turning static documents into queryable knowledge.

#### Measurable Criteria

| Coverage | Score | Status |
|----------|-------|--------|
| ≥80% of ADR files have valid frontmatter | 100 | pass |
| 50–79% have valid frontmatter | 60 | fail (partial) |
| <50% have valid frontmatter | 0 | fail |
| No ADR files found | — | skipped |

A frontmatter block is **valid** when:
- `status` is present and a non-empty string (any lifecycle value accepted, e.g. Proposed, Accepted, Implemented, Deprecated)
- `applies_to` is present and either a non-empty string or a list of non-empty strings (wildcards `"*"` are supported)

**Central ADR repository support**: repos that maintain ADRs in a shared central repository can configure `adr_source` in `.agentready-config.yaml` to point the assessor at the local clone. ADRs are then filtered by `applies_to` matching the assessed repo's name.

```yaml
# .agentready-config.yaml
adr_source:
repo: /path/to/local/clone # locally cloned central ADR repo
path: ADR # relative path within that repo
```

#### Remediation

Add a YAML frontmatter block to the top of each ADR file:

```markdown
---
status: Accepted
applies_to: my-service # or "*" for org-wide, or a list of service names
---
# ADR 0001: Title
...
```

Aim for ≥80% of ADR files to have valid frontmatter.

> **Note — config auto-discovery**: AgentReady does not yet auto-discover `.agentready-config.yaml` from the assessed repo's root (tracked in [#144](https://github.com/ambient-code/agentready/issues/144)). The `--config` flag must be passed explicitly when using the central ADR source feature. A repo maintainer committing `.agentready-config.yaml` to the repo root will not have it picked up automatically until #144 is resolved.

---

### Architectural Boundaries

**ID**: `architectural_boundaries`
Expand Down
15 changes: 11 additions & 4 deletions src/agentready/assessors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
v2.0.0: Updated with evidence-based rebalancing (ETH Zurich, Red Hat, Anthropic).
"""

from .adr_frontmatter import AdrFrontmatterAssessor
from .base import BaseAssessor
from .code_quality import (
CyclomaticComplexityAssessor,
Expand Down Expand Up @@ -54,7 +55,12 @@
)
from .verification import SingleFileVerificationAssessor

__all__ = ["create_all_assessors", "BaseAssessor", "LockFilesAssessor"]
__all__ = [
"create_all_assessors",
"BaseAssessor",
"LockFilesAssessor",
"AdrFrontmatterAssessor",
]


def create_all_assessors() -> list[BaseAssessor]:
Expand Down Expand Up @@ -91,12 +97,13 @@ def create_all_assessors() -> list[BaseAssessor]:
DesignIntentAssessor(), # 3% (moved from T3)
DbtDataTestsAssessor(), # dbt conditional
DbtProjectStructureAssessor(), # dbt conditional
# Tier 3 Important — 13% total (7 attributes)
ArchitectureDecisionsAssessor(), # 2%
# Tier 3 Important — 13% total (8 attributes)
ArchitectureDecisionsAssessor(), # 1%
AdrFrontmatterAssessor(), # 2% (2.4 - ADR Frontmatter Completeness)
OpenAPISpecsAssessor(), # 2%
CyclomaticComplexityAssessor(), # 2%
StructuredLoggingAssessor(), # 1%
ProgressiveDisclosureAssessor(), # 2% (moved from T4)
ProgressiveDisclosureAssessor(), # 1% (moved from T4)
ArchitecturalBoundaryAssessor(), # 2% (ADR B.1)
ThreatModelAssessor(), # 2% (ADR B.2)
# Tier 4 Advanced — 2% total (2 attributes, 1% each)
Expand Down
29 changes: 29 additions & 0 deletions src/agentready/assessors/_adr_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Shared helpers for ADR assessor modules.

Extracted to avoid a circular import between adr_frontmatter and adr_sources.
"""

import yaml


def parse_frontmatter(content: str) -> dict | None:
"""Extract YAML frontmatter from a markdown string.

Returns a dict if a valid frontmatter block is found, an empty dict
for an empty block, or None if no block exists, the YAML is invalid,
or the parsed value is not a mapping (e.g. a scalar or list).
"""
if not content.startswith("---"):
return None
end = content.find("---", 3)
if end == -1:
return None
try:
result = yaml.safe_load(content[3:end])
if result is None:
return {}
if not isinstance(result, dict):
return None
return result
except yaml.YAMLError:
return None
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Loading
Loading