Skip to content

fix: case-insensitive file detection for README, Makefile, and ADR directories#415

Merged
jwm4 merged 2 commits into
mainfrom
fix/case-sensitive-lookups-and-gnumakefile
May 12, 2026
Merged

fix: case-insensitive file detection for README, Makefile, and ADR directories#415
jwm4 merged 2 commits into
mainfrom
fix/case-sensitive-lookups-and-gnumakefile

Conversation

@jwm4
Copy link
Copy Markdown
Contributor

@jwm4 jwm4 commented May 12, 2026

Summary

Note: issue #373 (actionlint silent failure) was already resolved in #409.

Test plan

  • All 1152 existing tests pass
  • Assess renovatebot/renovate (lowercase readme.md) — should pass README check
  • Assess performancecopilot/pcp (GNUmakefile) — should pass one-command setup
  • Assess a repo with docs/ADRs/ — should pass architecture decisions check

Closes #387, #380, #379

🤖 Generated with Claude Code under the supervision of Bill Murdock

Summary by CodeRabbit

  • Bug Fixes
    • README detection is now case-insensitive and recognizes common filename variants, improving discovery and error messages.
    • Architecture decision discovery now checks a broader set of directory name variants and common locations for better detection.
    • Setup automation detection now recognizes GNU Make filenames in addition to existing makefile variants.

@jwm4 jwm4 requested a review from kami619 May 12, 2026 13:51
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

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: ca418763-8bd0-4f42-b627-4f5c6904a4b2

📥 Commits

Reviewing files that changed from the base of the PR and between 77efebe and fd2be32.

📒 Files selected for processing (2)
  • src/agentready/assessors/documentation.py
  • src/agentready/assessors/structure.py

📝 Walkthrough

Walkthrough

Case-insensitive discovery was added for README and ADR locations; README failure messages reference the discovered filename. ADR search prefers docs/ subdirs, then .adr, then root-level matches. OneCommandSetupAssessor now recognizes GNUmakefile in addition to existing makefile variants.

Changes

Documentation discovery and ADR detection

Layer / File(s) Summary
Case-insensitive README discovery
src/agentready/assessors/documentation.py
READMEAssessor.assess scans the repository root case-insensitively for README variants (readme.md, readme.rst, readme.txt, readme) and returns an immediate failure Finding with remediation when none are found. File reads and OSError messages use the discovered readme_path.name.
Case-insensitive ADR directory detection
src/agentready/assessors/documentation.py
ArchitectureDecisionsAssessor.assess searches for ADR directories by matching names (adr, adrs, decisions, architecture-decisions, specs, architecture, design) case-insensitively. Search order: matching docs/ subdirectories first, fallback to hidden .adr, then root-level matches. Failure evidence text now lists the expanded set of checked variants.

Setup automation file recognition

Layer / File(s) Summary
GNU Makefile recognition
src/agentready/assessors/structure.py
OneCommandSetupAssessor._check_setup_files adds GNUmakefile to the set of recognized setup automation filenames alongside makefile/Makefile.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits format with 'fix' type and clearly summarizes the main changes: case-insensitive file detection for README, Makefile, and ADR directories.
Linked Issues check ✅ Passed The pull request fully addresses issue #387 by implementing case-insensitive README detection via iterdir() scan, alongside related improvements for ADR and Makefile detection.
Out of Scope Changes check ✅ Passed All changes align with linked objectives: case-insensitive README discovery [#387], GNUmakefile recognition [#380], and ADR directory detection [#379].
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/case-sensitive-lookups-and-gnumakefile
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/case-sensitive-lookups-and-gnumakefile

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

📉 Test Coverage Report

Branch Coverage
This PR 72.9%
Main 73.0%
Diff ⚠️ -0.1%

Coverage calculated from unit tests only

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/documentation.py`:
- Around line 403-412: The README selection is nondeterministic because
repository.path.iterdir() isn't ordered; update the readme_path lookup to
iterate over a deterministic ordering (e.g., sorted(repository.path.iterdir(),
key=lambda f: f.name.lower())) so that readme_names matching (the readme_names
set and readme_path resolution) picks a predictable file; mirror the approach
used by ArchitectureDecisionsAssessor by sorting before filtering for is_file()
and name.lower() in readme_names.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 919c6d9e-b6df-4f90-baf4-d50cd956b843

📥 Commits

Reviewing files that changed from the base of the PR and between 9ada97c and 5c03fd6.

📒 Files selected for processing (2)
  • src/agentready/assessors/documentation.py
  • src/agentready/assessors/structure.py

Comment thread src/agentready/assessors/documentation.py Outdated
@jeremyeder jeremyeder force-pushed the fix/case-sensitive-lookups-and-gnumakefile branch from 5c03fd6 to 77efebe Compare May 12, 2026 14:39
@jwm4 jwm4 marked this pull request as draft May 12, 2026 14:42
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/agentready/assessors/documentation.py (1)

405-412: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make README variant selection deterministic.

When multiple README variants exist, plain iterdir() order is filesystem-dependent, so selected file can vary run-to-run. Sort candidates before next(...).

🤖 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 405 - 412, The README
selection using next(...) over repository.path.iterdir() is non-deterministic;
instead collect matching candidates (filtering with readme_names), sort them
(e.g., by name or another consistent key), and then pick the first element to
assign readme_path. Update the logic around the readme_path assignment that
currently uses repository.path.iterdir() so it builds a sorted list of files
before selecting the candidate.
🤖 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/documentation.py`:
- Around line 405-412: The README discovery can raise OSError from
repository.path.iterdir() and must be handled instead of crashing assess(); wrap
the iteration that assigns readme_path (the next(...) over
repository.path.iterdir()) in a try/except OSError block inside assess(), and on
exception return a Finding.error(...) (using the Finding class already used in
assess) that includes a clear message and the exception details; ensure you
still return None or proceed normally when no error occurs so subsequent logic
using readme_path behaves as before.
- Around line 573-589: The iterdir calls over docs_dir and repository.path can
raise OSError and must be guarded; wrap each sorted(...iterdir()) usage (the
loop that searches for adr_dir using docs_dir.iterdir() and the fallback loop
using repository.path.iterdir()) in try/except OSError blocks (or extract a
small helper like safe_iterdir) and on exception do not raise—record/return an
appropriate skipped/error Finding and leave adr_dir unset so the assessor
continues gracefully; reference the variables docs_dir, repository.path,
adr_dir, and adr_target_names when applying the fix.

---

Duplicate comments:
In `@src/agentready/assessors/documentation.py`:
- Around line 405-412: The README selection using next(...) over
repository.path.iterdir() is non-deterministic; instead collect matching
candidates (filtering with readme_names), sort them (e.g., by name or another
consistent key), and then pick the first element to assign readme_path. Update
the logic around the readme_path assignment that currently uses
repository.path.iterdir() so it builds a sorted list of files before selecting
the candidate.
🪄 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: 89da69e2-9a8e-47ca-a068-292525d5276a

📥 Commits

Reviewing files that changed from the base of the PR and between 5c03fd6 and 77efebe.

📒 Files selected for processing (2)
  • src/agentready/assessors/documentation.py
  • src/agentready/assessors/structure.py

Comment thread src/agentready/assessors/documentation.py Outdated
Comment on lines +573 to +589
for candidate in sorted(docs_dir.iterdir()):
if candidate.is_dir() and candidate.name.lower() in adr_target_names:
adr_dir = candidate
break

# Fall back to repo root and hidden .adr
if not adr_dir:
if (repository.path / ".adr").is_dir():
adr_dir = repository.path / ".adr"
else:
for candidate in sorted(repository.path.iterdir()):
if (
candidate.is_dir()
and candidate.name.lower() in adr_target_names
):
adr_dir = candidate
break
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard ADR directory scans against OSError.

Both sorted(docs_dir.iterdir()) and sorted(repository.path.iterdir()) may raise OSError; this would crash the assessor before it can emit a fail/error finding.

Suggested fix
-        if docs_dir.is_dir():
-            for candidate in sorted(docs_dir.iterdir()):
-                if candidate.is_dir() and candidate.name.lower() in adr_target_names:
-                    adr_dir = candidate
-                    break
+        if docs_dir.is_dir():
+            try:
+                for candidate in sorted(docs_dir.iterdir()):
+                    if candidate.is_dir() and candidate.name.lower() in adr_target_names:
+                        adr_dir = candidate
+                        break
+            except OSError as e:
+                return Finding.error(
+                    self.attribute, reason=f"Could not scan docs/ for ADR directories: {e}"
+                )
@@
-                for candidate in sorted(repository.path.iterdir()):
-                    if (
-                        candidate.is_dir()
-                        and candidate.name.lower() in adr_target_names
-                    ):
-                        adr_dir = candidate
-                        break
+                try:
+                    for candidate in sorted(repository.path.iterdir()):
+                        if (
+                            candidate.is_dir()
+                            and candidate.name.lower() in adr_target_names
+                        ):
+                            adr_dir = candidate
+                            break
+                except OSError as e:
+                    return Finding.error(
+                        self.attribute,
+                        reason=f"Could not scan repository root for ADR directories: {e}",
+                    )

As per coding guidelines "Check for proper error handling (return skipped/error Finding, don't crash)."

🤖 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 573 - 589, The
iterdir calls over docs_dir and repository.path can raise OSError and must be
guarded; wrap each sorted(...iterdir()) usage (the loop that searches for
adr_dir using docs_dir.iterdir() and the fallback loop using
repository.path.iterdir()) in try/except OSError blocks (or extract a small
helper like safe_iterdir) and on exception do not raise—record/return an
appropriate skipped/error Finding and leave adr_dir unset so the assessor
continues gracefully; reference the variables docs_dir, repository.path,
adr_dir, and adr_target_names when applying the fix.

@jeremyeder jeremyeder force-pushed the fix/case-sensitive-lookups-and-gnumakefile branch from 77efebe to 8440e83 Compare May 12, 2026 14:42
@jwm4 jwm4 force-pushed the fix/case-sensitive-lookups-and-gnumakefile branch from 8440e83 to 0461eb2 Compare May 12, 2026 14:59
- READMEAssessor: find README.md/rst/txt case-insensitively via iterdir()
  instead of hardcoded path (closes #387)
- OneCommandSetupAssessor: recognize GNUmakefile and makefile in addition
  to Makefile (closes #380)
- ArchitectureDecisionsAssessor: scan docs/ and repo root for ADR
  directories case-insensitively (closes #379)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jwm4 jwm4 force-pushed the fix/case-sensitive-lookups-and-gnumakefile branch from 0461eb2 to 960e005 Compare May 12, 2026 15:22
@jwm4 jwm4 marked this pull request as ready for review May 12, 2026 15:23
- README scan: sort candidates before next() so selection is deterministic
  when multiple variants exist (e.g. readme.md and README.rst both present)
- README scan: wrap iterdir() in try/except OSError, return Finding.error
  instead of crashing
- ADR scan: wrap both docs/ and root iterdir() calls in try/except OSError,
  fall through gracefully rather than propagating

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jwm4
Copy link
Copy Markdown
Contributor Author

jwm4 commented May 12, 2026

Real-World Validation

Tested this PR against 7 repositories to verify the three fixes. Here are the results.


README case-insensitive detection (#387)

renovatebot/renovate (readme.md lowercase): README check now passes. Previously would have missed this. Fix confirmed working.


GNUmakefile detection (#380)

performancecopilot/pcp (GNUmakefile at root): One-command setup check now passes. Fix confirmed working.


ADR directory scanning, including specs (#379, #414)

Tested 5 repos to evaluate the specs candidate directory name specifically, since it is the most ambiguous addition to the list.

Repo Directory matched Result Verdict
performancecopilot/pcp docs/specs Pass (score 40+) False positive - contains formal API/interface specs, not decision records
devcontainers/spec docs/specs Pass (score 82) Arguable - 18 formal spec .md files, low template compliance
liatrio-labs/spec-driven-workflow docs/specs Fail (score 40) False negative - genuine ADR-style content exists, but in numbered subdirectories (01-spec-.../) that the assessor does not recurse into
github/spec-kit (none matched) Fail (score 0) True negative - no ADR directory present, correct result
bdfinst/agentic-dev-team docs/specs Pass (score 83) True positive - 5 design/decision records for agentic components (e.g. beads-workflow-plugin.md, subagent-report-write-contract.md)

Assessment of specs as a directory name: there is genuine signal here. bdfinst/agentic-dev-team is using docs/specs exactly as intended - to give agents background on why the system is the way it is. The pcp case is a real false positive, but it reflects a fundamental ambiguity in the name specs rather than a flaw in the detection logic. Other names on the list (e.g. decisions, architecture-decisions) carry the same risk in different repos. On balance, keeping specs in the list seems reasonable.

Separate issue - subdirectory depth: the liatrio-labs false negative is not specific to specs. The assessor only counts .md files directly inside the matched directory, so any repo that organizes its ADRs into numbered subdirectories will score zero regardless of which directory name is used. Filed as #423 for follow-up.


Summary

All three fixes work as intended on real repos. No regressions observed. The specs addition produces mixed results, but the true positive case (bdfinst) justifies keeping it. The subdirectory scanning limitation is a pre-existing gap tracked in #423.


Reviewed with Claude Code under the supervision of Bill Murdock.

@jwm4 jwm4 merged commit f6c5197 into main May 12, 2026
6 checks passed
@jwm4 jwm4 deleted the fix/case-sensitive-lookups-and-gnumakefile branch May 12, 2026 16:59
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.36.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] No README found because it's not UPPERCASE

1 participant