Skip to content

feat: add adr_frontmatter_completeness assessor with central ADR repo support#514

Open
Dannyb48 wants to merge 1 commit into
ambient-code:mainfrom
Dannyb48:feat/adr-frontmatter-central-repo
Open

feat: add adr_frontmatter_completeness assessor with central ADR repo support#514
Dannyb48 wants to merge 1 commit into
ambient-code:mainfrom
Dannyb48:feat/adr-frontmatter-central-repo

Conversation

@Dannyb48

@Dannyb48 Dannyb48 commented Jun 25, 2026

Copy link
Copy Markdown

Description

Adds a new adr_frontmatter_completeness assessor (Tier 3, 2% weight) that scores repositories on whether ADR files contain structured YAML frontmatter with required status and applies_to fields. Also adds central ADR repository support so teams that maintain ADRs in a shared repo are not penalised, and fixes ArchitectureDecisionsAssessor to respect that same config.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • [X ] New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Test coverage improvement

Related Issues

Fixes #512
Fixes #513

Changes Made

  • Added adr_frontmatter_completeness assessor (src/agentready/assessors/adr_frontmatter.py) — scores repos on YAML frontmatter coverage across ADR files; thresholds: ≥80% → pass, 50–79% → partial, <50% → fail
  • Added LocalAdrSource and CentralAdrSource (src/agentready/assessors/adr_sources.py) — LocalAdrSource discovers ADR directories on the local repo filesystem; CentralAdrSource reads ADRs from a locally cloned shared repo and filters by applies_to (supports wildcard, short name, full org/repo, and list matching)
  • Extended Config model with adr_source: dict | None field for central repo configuration (repo + path keys)
  • Fixed ArchitectureDecisionsAssessor (src/agentready/assessors/documentation.py) to pass when adr_source is configured and the central repo path exists, rather than failing repos that intentionally maintain ADRs centrally
  • Registered AdrFrontmatterAssessor in assessors/__init__.py
  • Added adr_frontmatter_completeness: 0.02 weight to default-weights.yaml; rebalanced by reducing architecture_decisions (2% → 1%) and progressive_disclosure (2% → 1%) to keep total at 1.0

Testing

  • [X ] Unit tests pass (pytest)
  • Integration tests pass
  • [ X] Manual testing performed
  • [ X] No new warnings or errors

Checklist

  • [ X] My code follows the project's code style
  • [X ] I have performed a self-review of my own code
  • [ X] I have commented my code, particularly in hard-to-understand areas
  • [ X] I have made corresponding changes to the documentation
  • [X ] My changes generate no new warnings
  • [X ] I have added tests that prove my fix is effective or that my feature works
  • [ X] New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Screenshots (if applicable)

Additional Notes

Central ADR repo config (.agentready-config.yaml):

adr_source:
  repo: /path/to/local/clone   # locally cloned central ADR repo
  path: ADR                    # relative path within that repo

No network calls are made — the central repo must be locally cloned. The assessor gracefully skips if the path does not exist or no ADRs match the assessed repo's name.

Valid status values cover MADR (Proposed, Accepted, Implementable, Implemented, Replaced, Deprecated, Superseded, Approved) and adr-tools (active, superseded, draft) conventions to accommodate common ADR tooling.

Summary by CodeRabbit

  • New Features
    • Added ADR frontmatter completeness scoring (required status/applies_to) with remediation guidance.
    • Added centralized ADR source support with repo-aware applies_to matching and evidence.
    • Added configuration for centralized ADR repository settings.
  • Bug Fixes
    • Improved Architecture Decisions assessment to award full credit when applicable centralized ADRs are found.
  • Chores
    • Rebalanced Tier 3 weights, added the new adr_frontmatter_completeness attribute, and adjusted several default weights (including progressive disclosure and structured logging).
  • Tests
    • Added comprehensive unit coverage for local/central ADR discovery, frontmatter parsing/classification/scoring, and updated weight expectations.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 8a32ae0f-d0fb-4c69-af3d-d816ad703abd

📥 Commits

Reviewing files that changed from the base of the PR and between c211a26 and 687c4d2.

📒 Files selected for processing (13)
  • src/agentready/assessors/__init__.py
  • src/agentready/assessors/_adr_utils.py
  • src/agentready/assessors/adr_frontmatter.py
  • src/agentready/assessors/adr_sources.py
  • src/agentready/assessors/code_quality.py
  • src/agentready/assessors/documentation.py
  • src/agentready/assessors/patterns.py
  • src/agentready/data/default-weights.yaml
  • src/agentready/models/config.py
  • tests/unit/test_adr_central_source.py
  • tests/unit/test_adr_frontmatter.py
  • tests/unit/test_assessors_documentation.py
  • tests/unit/test_assessors_patterns.py

📝 Walkthrough

Walkthrough

Adds ADR frontmatter scoring, central ADR source support, assessor wiring, and Tier 3 weight updates. Documentation assessment now falls back to configured central ADRs when local ADR files are absent.

Changes

ADR frontmatter and central source support

Layer / File(s) Summary
Config and ADR sources
src/agentready/models/config.py, src/agentready/assessors/adr_sources.py, tests/unit/test_adr_central_source.py
adr_source is added to Config, and local/central ADR discovery helpers plus matching tests are added.
Frontmatter parsing and scoring
src/agentready/assessors/_adr_utils.py, src/agentready/assessors/adr_frontmatter.py, tests/unit/test_adr_frontmatter.py
ADR frontmatter parsing, file classification, coverage scoring, the new assessor, and its tests are added.
Assessor wiring and central fallback
src/agentready/assessors/documentation.py, tests/unit/test_assessors_documentation.py
ArchitectureDecisionsAssessor now checks configured central ADRs before local fallback, with tests for pass, skipped, and fail outcomes.
Registry and weights
src/agentready/assessors/__init__.py, src/agentready/data/default-weights.yaml, src/agentready/assessors/code_quality.py, src/agentready/assessors/patterns.py, tests/unit/test_assessors_patterns.py
AdrFrontmatterAssessor is exported and registered, and Tier 3 weights and related expectations are rebalanced.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning Missing the required Conventional Commits scope; it is feat: ... instead of feat(scope): .... Add a scope in parentheses, e.g. feat(assessors): add adr_frontmatter_completeness assessor with central ADR repo support.
Out of Scope Changes check ⚠️ Warning READMEAssessor string-only reformatting is unrelated to the ADR objectives and adds unnecessary churn. Move that formatting into a separate cleanup PR or revert it unless it is required for the ADR feature work.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The new ADR frontmatter assessor, central ADR source config, and fallback behavior align with #512 and #513.
Docstring Coverage ✅ Passed Docstring coverage is 96.08% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/agentready/assessors/adr_sources.py`:
- Around line 63-73: The frontmatter parsing logic in _parse_frontmatter_content
duplicates parse_frontmatter from adr_frontmatter, so replace the local YAML
parsing helper with the shared public parser. Update CentralAdrSource to import
and call parse_frontmatter directly, and if adr_frontmatter already depends on
adr_sources, move the parser into a small shared module to avoid an import
cycle.

In `@src/agentready/assessors/documentation.py`:
- Around line 753-793: The _check_central_adr_source method is awarding full
credit too early by only checking repository.config.adr_source and
local_path.exists(), while never validating the computed adr_dir contents.
Update this path in DocumentationAssessor to require the central ADR directory
to exist and to contain relevant ADR files (or matching entries, consistent with
AdrFrontmatterAssessor._assess_central and its applies_to filtering) before
returning a pass Finding with score=100.0. If the configured subpath is missing,
empty, or has no applicable ADRs, fall through and return None so the normal
failure path is used.

In `@src/agentready/data/default-weights.yaml`:
- Around line 61-67: The Tier 3 default weights in the YAML are now the source
of truth, but several assessor classes still have mismatched
attribute.default_weight values. Update the defaults in
ArchitectureDecisionsAssessor, OpenAPISpecsAssessor, StructuredLoggingAssessor,
and ProgressiveDisclosureAssessor to match default-weights.yaml, or refactor
them to read from one canonical weight source so scoring stays consistent
everywhere.

In `@tests/unit/test_assessors_documentation.py`:
- Around line 711-748: Add a regression test for ArchitectureDecisionsAssessor
that uses Config.adr_source pointing to an existing central repo root with a
present ADR path that is empty or contains no matching ADR files, and assert
assess(repo) falls through to the normal failure result instead of passing with
100.0; place it alongside test_passes_when_central_adr_source_configured and
test_falls_through_when_central_path_missing to cover the
_check_central_adr_source behavior when the central source exists but has no
valid ADRs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 9d89ab66-30f7-4160-883c-36cbb092869b

📥 Commits

Reviewing files that changed from the base of the PR and between 5ae94eb and 04d63da.

📒 Files selected for processing (9)
  • src/agentready/assessors/__init__.py
  • src/agentready/assessors/adr_frontmatter.py
  • src/agentready/assessors/adr_sources.py
  • src/agentready/assessors/documentation.py
  • src/agentready/data/default-weights.yaml
  • src/agentready/models/config.py
  • tests/unit/test_adr_central_source.py
  • tests/unit/test_adr_frontmatter.py
  • tests/unit/test_assessors_documentation.py

Comment thread src/agentready/assessors/adr_sources.py Outdated
Comment thread src/agentready/assessors/documentation.py
Comment thread src/agentready/data/default-weights.yaml
Comment thread tests/unit/test_assessors_documentation.py
@Dannyb48 Dannyb48 force-pushed the feat/adr-frontmatter-central-repo branch from 04d63da to d79d26f Compare June 25, 2026 02:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/agentready/assessors/documentation.py (1)

569-574: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stale weight in docstring. The class docstring still says "Tier 3 Important (3% weight)" but default_weight is now 0.01. Same drift exists on OpenAPISpecsAssessor (Line 1360 says 3%, weight is now 0.02). Update both to avoid misleading future readers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agentready/assessors/documentation.py` around lines 569 - 574, Update the
class docstrings for ArchitectureDecisionsAssessor and OpenAPISpecsAssessor so
the stated tier/weight matches their current default_weight values. The
ArchitectureDecisionsAssessor docstring should no longer say 3% weight and must
reflect 0.01, and the OpenAPISpecsAssessor docstring should no longer say 3%
weight and must reflect 0.02. Keep the wording aligned with the existing
assessor class documentation style.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/agentready/assessors/_adr_utils.py`:
- Around line 20-23: The frontmatter parser in _adr_utils.load_frontmatter
currently returns any yaml.safe_load result, which can be a scalar or list and
then breaks callers that expect mapping access. Update this helper to enforce a
dict-or-None contract by returning the parsed value only when it is a dict,
otherwise return None, and keep the existing YAML error handling. This will let
classify_adr_file and CentralAdrSource.get_matching_adr_files continue to skip
invalid ADRs gracefully instead of hitting AttributeError on fm.get(...).

---

Outside diff comments:
In `@src/agentready/assessors/documentation.py`:
- Around line 569-574: Update the class docstrings for
ArchitectureDecisionsAssessor and OpenAPISpecsAssessor so the stated tier/weight
matches their current default_weight values. The ArchitectureDecisionsAssessor
docstring should no longer say 3% weight and must reflect 0.01, and the
OpenAPISpecsAssessor docstring should no longer say 3% weight and must reflect
0.02. Keep the wording aligned with the existing assessor class documentation
style.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: c6641b62-3126-48f2-9f75-751cd4fa4f4e

📥 Commits

Reviewing files that changed from the base of the PR and between 04d63da and d79d26f.

📒 Files selected for processing (13)
  • src/agentready/assessors/__init__.py
  • src/agentready/assessors/_adr_utils.py
  • src/agentready/assessors/adr_frontmatter.py
  • src/agentready/assessors/adr_sources.py
  • src/agentready/assessors/code_quality.py
  • src/agentready/assessors/documentation.py
  • src/agentready/assessors/patterns.py
  • src/agentready/data/default-weights.yaml
  • src/agentready/models/config.py
  • tests/unit/test_adr_central_source.py
  • tests/unit/test_adr_frontmatter.py
  • tests/unit/test_assessors_documentation.py
  • tests/unit/test_assessors_patterns.py

Comment thread src/agentready/assessors/_adr_utils.py
@Dannyb48 Dannyb48 force-pushed the feat/adr-frontmatter-central-repo branch from d79d26f to 0cc67e5 Compare June 25, 2026 03:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/agentready/data/default-weights.yaml (1)

3-3: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stale attribute count. Tier 3 grew to 8, so totals are now 9+9+8+2 = 28 attributes, not 27. Update this header to match the Tier 3 change on Line 12.

📝 Fix
-# This file defines the default weights for all 27 attributes.
+# This file defines the default weights for all 28 attributes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agentready/data/default-weights.yaml` at line 3, The header comment in
default-weights.yaml has a stale total attribute count; update the “default
weights” summary to reflect the current 28 attributes after the Tier 3 increase.
Keep the comment consistent with the tier breakdown used in this file,
especially the Tier 3 definition in the same YAML, so the documented total
matches the actual 9+9+8+2 distribution.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/agentready/assessors/adr_frontmatter.py`:
- Around line 207-265: The scoring in _score_files currently uses the external
score_from_coverage helper; switch it to self.calculate_proportional_score() so
ADR frontmatter scoring follows the shared BaseAssessor domain logic. Update the
score assignment in _score_files to derive the final score from
calculate_proportional_score using the valid and total counts, and keep the rest
of the Finding construction unchanged.

---

Outside diff comments:
In `@src/agentready/data/default-weights.yaml`:
- Line 3: The header comment in default-weights.yaml has a stale total attribute
count; update the “default weights” summary to reflect the current 28 attributes
after the Tier 3 increase. Keep the comment consistent with the tier breakdown
used in this file, especially the Tier 3 definition in the same YAML, so the
documented total matches the actual 9+9+8+2 distribution.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 749841ad-77bc-4e17-85e2-6c65c90647f0

📥 Commits

Reviewing files that changed from the base of the PR and between d79d26f and 0cc67e5.

📒 Files selected for processing (13)
  • src/agentready/assessors/__init__.py
  • src/agentready/assessors/_adr_utils.py
  • src/agentready/assessors/adr_frontmatter.py
  • src/agentready/assessors/adr_sources.py
  • src/agentready/assessors/code_quality.py
  • src/agentready/assessors/documentation.py
  • src/agentready/assessors/patterns.py
  • src/agentready/data/default-weights.yaml
  • src/agentready/models/config.py
  • tests/unit/test_adr_central_source.py
  • tests/unit/test_adr_frontmatter.py
  • tests/unit/test_assessors_documentation.py
  • tests/unit/test_assessors_patterns.py

Comment thread src/agentready/assessors/adr_frontmatter.py
@Dannyb48 Dannyb48 force-pushed the feat/adr-frontmatter-central-repo branch 3 times, most recently from 4f5c484 to c211a26 Compare June 25, 2026 14:40

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/agentready/assessors/adr_frontmatter.py`:
- Around line 61-69: The `assess_completeness` logic in `adr_frontmatter.py`
treats any non-empty `applies_to` list as complete, but `_applies_to_matches()`
only supports string entries, so invalid list members can slip through. Update
the `applies_to` validation in `assess_completeness` to require list items to be
strings and non-empty (matching the matcher contract used by
`_applies_to_matches`), and mark the ADR incomplete when the list contains any
non-string or blank entries.
- Around line 54-59: The ADR frontmatter check in assess_frontmatter is too
strict because it treats any status outside VALID_STATUSES as incomplete. Update
the status validation so it only verifies that status exists and is a non-empty
string, and stop rejecting unfamiliar lifecycle values in adr_frontmatter.py;
keep the applies_to/frontmatter presence checks unchanged.

In `@src/agentready/assessors/adr_sources.py`:
- Around line 99-106: The adr source path handling in AdrSource.adr_dir
currently trusts adr_path directly, allowing relative escapes or absolute paths
to point outside the cloned repo root. Update AdrSource.__init__ and/or adr_dir
so path values are normalized and validated against local_path, rejecting any
input that resolves outside the repo clone and only allowing subpaths within the
central ADR directory.

In `@src/agentready/assessors/documentation.py`:
- Around line 655-660: The central ADR source check in
DocumentationAssessor._assess_central is returning None for
configured-but-unavailable external repos, which then falls through to the
generic no-ADR failure. Update the handling around _check_central_adr_source so
that when a configured adr_source cannot be read or the repo/path is missing, it
returns a skipped/warning Finding instead of None; keep None reserved for “not
configured” or “not applicable,” and align the behavior with
AdrFrontmatterAssessor._assess_central and the central_finding branch in
_assess_central.

In `@src/agentready/models/config.py`:
- Around line 60-69: `adr_source` is currently an untyped free-form dict, so
invalid shapes and non-string values slip through and later crash the assessors.
Update the config model in `Config` to use a validated nested object for
`adr_source` with explicit `repo` and `path` fields, and add validation to
reject absolute or otherwise invalid `path` values up front so
`documentation.py` and `adr_sources.py` only receive safe strings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 88384f6b-73ab-4e8c-b42b-f6841f6ca7ca

📥 Commits

Reviewing files that changed from the base of the PR and between 0cc67e5 and c211a26.

📒 Files selected for processing (13)
  • src/agentready/assessors/__init__.py
  • src/agentready/assessors/_adr_utils.py
  • src/agentready/assessors/adr_frontmatter.py
  • src/agentready/assessors/adr_sources.py
  • src/agentready/assessors/code_quality.py
  • src/agentready/assessors/documentation.py
  • src/agentready/assessors/patterns.py
  • src/agentready/data/default-weights.yaml
  • src/agentready/models/config.py
  • tests/unit/test_adr_central_source.py
  • tests/unit/test_adr_frontmatter.py
  • tests/unit/test_assessors_documentation.py
  • tests/unit/test_assessors_patterns.py

Comment on lines +54 to +59
status = fm.get("status")
applies_to = fm.get("applies_to")

# status must be present, non-empty string, and in the allowed set
if not status or not isinstance(status, str) or status not in VALID_STATUSES:
return "incomplete"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don't reject ADRs just because status isn't in this hard-coded set.

Line 58 turns any non-empty but unfamiliar status into "incomplete", but this feature is supposed to verify structured frontmatter presence. That will score down repos using valid but different lifecycle terms such as accepted, obsolete, or org-specific statuses.

Suggested fix
-    # status must be present, non-empty string, and in the allowed set
-    if not status or not isinstance(status, str) or status not in VALID_STATUSES:
+    # status must be present and non-empty
+    if not isinstance(status, str) or not status.strip():
         return "incomplete"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
status = fm.get("status")
applies_to = fm.get("applies_to")
# status must be present, non-empty string, and in the allowed set
if not status or not isinstance(status, str) or status not in VALID_STATUSES:
return "incomplete"
status = fm.get("status")
applies_to = fm.get("applies_to")
# status must be present and non-empty
if not isinstance(status, str) or not status.strip():
return "incomplete"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agentready/assessors/adr_frontmatter.py` around lines 54 - 59, The ADR
frontmatter check in assess_frontmatter is too strict because it treats any
status outside VALID_STATUSES as incomplete. Update the status validation so it
only verifies that status exists and is a non-empty string, and stop rejecting
unfamiliar lifecycle values in adr_frontmatter.py; keep the
applies_to/frontmatter presence checks unchanged.

Comment on lines +61 to +69
# applies_to must be present and non-empty (string or non-empty list)
if applies_to is None:
return "incomplete"
if not isinstance(applies_to, (str, list)):
return "incomplete"
if isinstance(applies_to, str) and not applies_to.strip():
return "incomplete"
if isinstance(applies_to, list) and not applies_to:
return "incomplete"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate applies_to list members against the matcher contract.

Line 68 accepts any non-empty list as valid, but _applies_to_matches() only knows how to match strings. For example, applies_to: [123] passes completeness locally and can never match centrally, so the same ADR is "valid" in one path and ignored in the other.

Suggested fix
-    if isinstance(applies_to, list) and not applies_to:
-        return "incomplete"
+    if isinstance(applies_to, list) and (
+        not applies_to
+        or any(not isinstance(item, str) or not item.strip() for item in applies_to)
+    ):
+        return "incomplete"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# applies_to must be present and non-empty (string or non-empty list)
if applies_to is None:
return "incomplete"
if not isinstance(applies_to, (str, list)):
return "incomplete"
if isinstance(applies_to, str) and not applies_to.strip():
return "incomplete"
if isinstance(applies_to, list) and not applies_to:
return "incomplete"
# applies_to must be present and non-empty (string or non-empty list)
if applies_to is None:
return "incomplete"
if not isinstance(applies_to, (str, list)):
return "incomplete"
if isinstance(applies_to, str) and not applies_to.strip():
return "incomplete"
if isinstance(applies_to, list) and (
not applies_to
or any(not isinstance(item, str) or not item.strip() for item in applies_to)
):
return "incomplete"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agentready/assessors/adr_frontmatter.py` around lines 61 - 69, The
`assess_completeness` logic in `adr_frontmatter.py` treats any non-empty
`applies_to` list as complete, but `_applies_to_matches()` only supports string
entries, so invalid list members can slip through. Update the `applies_to`
validation in `assess_completeness` to require list items to be strings and
non-empty (matching the matcher contract used by `_applies_to_matches`), and
mark the ADR incomplete when the list contains any non-string or blank entries.

Comment on lines +99 to +106
def __init__(self, local_path: Path, adr_path: str) -> None:
self._local_path = local_path
self._adr_path = adr_path.rstrip("/")

@property
def adr_dir(self) -> Path:
"""Absolute path to the ADR subdirectory inside the central repo."""
return self._local_path / self._adr_path

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Constrain adr_source.path to the cloned repo root.

Line 106 joins config input directly, so path values like ../../notes or an absolute path escape local_path. In the current flow, an untrusted repo config can make the assessor read arbitrary local .md files instead of only the central ADR clone.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agentready/assessors/adr_sources.py` around lines 99 - 106, The adr
source path handling in AdrSource.adr_dir currently trusts adr_path directly,
allowing relative escapes or absolute paths to point outside the cloned repo
root. Update AdrSource.__init__ and/or adr_dir so path values are normalized and
validated against local_path, rejecting any input that resolves outside the repo
clone and only allowing subpaths within the central ADR directory.

Comment on lines +655 to +660
# Check for central ADR repo configured via adr_source config.
# If the org maintains ADRs centrally, the repo still gets full credit.
central_finding = self._check_central_adr_source(repository)
if central_finding is not None:
return central_finding

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don't convert a configured-but-unavailable central ADR source into a 0-point fail.

When Line 773 or Line 779 returns None, Line 657 falls through to the generic no-ADR failure path. That penalizes repositories for a missing/unreadable external ADR clone instead of degrading gracefully. AdrFrontmatterAssessor._assess_central() already returns Finding.skipped(...) for the same "central repo/path missing" cases, so this assessor should reserve None for "not configured" or "no ADRs apply" and return a skipped/warning finding when the configured source cannot be read.

Minimal fix
         if not local_path.exists():
-            return None
+            return Finding.skipped(
+                self.attribute,
+                f"Central ADR repo not found at {local_path}",
+            )

         adr_path = adr_source.get("path") or "ADR"
         source = CentralAdrSource(local_path=local_path, adr_path=adr_path)

         if not source.adr_dir.is_dir():
-            return None
+            return Finding.skipped(
+                self.attribute,
+                f"Central ADR directory not found at {source.adr_dir}",
+            )

As per path instructions, src/agentready/assessors/**: "Check for proper error handling (return skipped/error Finding, don't crash)" and "Check for graceful degradation when tools missing."

Also applies to: 752-800

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agentready/assessors/documentation.py` around lines 655 - 660, The
central ADR source check in DocumentationAssessor._assess_central is returning
None for configured-but-unavailable external repos, which then falls through to
the generic no-ADR failure. Update the handling around _check_central_adr_source
so that when a configured adr_source cannot be read or the repo/path is missing,
it returns a skipped/warning Finding instead of None; keep None reserved for
“not configured” or “not applicable,” and align the behavior with
AdrFrontmatterAssessor._assess_central and the central_finding branch in
_assess_central.

Source: Path instructions

Comment on lines +60 to +69
adr_source: Annotated[
dict | None,
Field(
default=None,
description=(
"Central ADR repository config: "
"{'repo': '/path/to/local/clone', 'path': 'ADR/'}"
),
),
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Validate adr_source as a typed nested config.

This field accepts any mapping shape/value types, but the consumers assume strings. A config like adr_source: {repo: 123, path: 456} will pass here, then src/agentready/assessors/documentation.py Line 772 calls Path(local_path_str) and src/agentready/assessors/adr_sources.py strips adr_path with .rstrip("/"), which raises during assessment. Please model adr_source as a validated object (repo, path) and reject absolute/invalid path values up front so bad config fails validation instead of crashing the assessors.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/agentready/models/config.py` around lines 60 - 69, `adr_source` is
currently an untyped free-form dict, so invalid shapes and non-string values
slip through and later crash the assessors. Update the config model in `Config`
to use a validated nested object for `adr_source` with explicit `repo` and
`path` fields, and add validation to reject absolute or otherwise invalid `path`
values up front so `documentation.py` and `adr_sources.py` only receive safe
strings.

…al ADR repo support (ambient-code#512, ambient-code#513)

Adds a new `adr_frontmatter_completeness` assessor (Tier 3, 2% weight) that
scores repos on whether ADR files contain structured YAML frontmatter with
required `status` and `applies_to` fields. Supports both local ADR directories
and a centrally maintained ADR repository via config.

## New assessor: adr_frontmatter_completeness

Scoring:
- ≥80% of ADR files have valid frontmatter → pass (100)
- 50–79% → partial/fail (60)
- <50% → fail (0)
- No ADR files found → skipped

Valid statuses cover MADR (Proposed, Accepted, Implementable, Implemented,
Replaced, Deprecated, Superseded, Approved) and adr-tools (active, superseded,
draft) conventions.

## Central ADR repository support

Configure via `.agentready-config.yaml`:

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

When configured, ADRs are read from the central repo and filtered by their
`applies_to` frontmatter field matching the assessed repo name. Supports
wildcard (`"*"`), short name, full org/repo, and list matching.

No network calls — the central repo must be locally cloned. Gracefully skips
if the path does not exist or no ADRs match.

## Fix: architecture_decisions respects central ADR repos

`ArchitectureDecisionsAssessor` now passes when `adr_source` is configured and
the central repo path exists and contains ADRs that match the repo via
applies_to, rather than penalising repos that deliberately maintain ADRs
centrally.

## Weight rebalancing (Tier 3)

Added `adr_frontmatter_completeness: 0.02`. Funded by reducing
`architecture_decisions` (2% → 1%) and `progressive_disclosure` (2% → 1%).
Also synced `default_weight` values in ArchitectureDecisionsAssessor,
OpenAPISpecsAssessor, StructuredLoggingAssessor, and ProgressiveDisclosureAssessor
to match default-weights.yaml. Total weights remain 1.0.

## Shared utility module

Extracted `parse_frontmatter` into `assessors/_adr_utils.py` to avoid a
circular import between `adr_frontmatter` and `adr_sources`.

Closes ambient-code#512, ambient-code#513

Co-authored-by: Cursor <cursoragent@cursor.com>
@Dannyb48 Dannyb48 force-pushed the feat/adr-frontmatter-central-repo branch from c211a26 to 687c4d2 Compare June 25, 2026 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant