Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
117f2f0
classify: split link-reached files — referenced (markdown) vs generic…
cleverhoods May 19, 2026
cc26860
check: ails check referenced — virtual capability for link-reached files
cleverhoods May 19, 2026
947a3ff
docs: rules-cli page + cross-links + UNRELEASED entry (Refs #29)
cleverhoods May 19, 2026
24fb3e8
rules: ails list checks — adapter + Typer subcommand + tests (Refs #29)
cleverhoods May 19, 2026
38351c8
check: strict-main fold — drop nested from `ails check main`
cleverhoods May 20, 2026
cc58ebd
check: narrow discovery to single-file target arg
cleverhoods May 20, 2026
f63dd6a
config: merge global `~/.reporails/config.yml` into project config
cleverhoods May 20, 2026
2a8651c
mcp/json: thread tier + per-finding category + surface category_break…
cleverhoods May 20, 2026
505533c
mcp: trim surface — drop score/heal, add preflight, accept file targe…
cleverhoods May 20, 2026
79d196f
rules: rule.md frontmatter fix: field — canonical operator-facing fix…
cleverhoods May 20, 2026
60e24ec
tests/skills: /ails subagent validation procedure + fixture
cleverhoods May 20, 2026
3c1e9d1
cli: variadic check targets + heal fold into --heal flag + -h/--help …
cleverhoods May 20, 2026
19a5b0d
cli: ails rules verb + completion module + repeatable --capability fi…
cleverhoods May 20, 2026
0cc50b8
rules: project-description-present — heal verb in example reflects --…
cleverhoods May 20, 2026
7aee29e
docs: 0.5.11 surface alignment — rules verb examples, index links, fr…
cleverhoods May 20, 2026
7cb1859
check: friendly @-form hint when bare capability token misses path re…
cleverhoods May 20, 2026
ecf2249
check: symlink-follow .md discovery + surface unresolved subagent skills
cleverhoods May 22, 2026
de6eb7e
check: scope mechanical args.path globs to capability-narrowed classi…
cleverhoods May 23, 2026
4599ca7
check: scope-aware aggregate rules + skill-entrypoint redesign (0.5.1…
cleverhoods May 28, 2026
168e9c3
release: bump version to 0.5.11
cleverhoods May 28, 2026
1fe7818
build: remove unused internal scripts; extend ignore rules for non-sh…
cleverhoods May 29, 2026
2132ce9
classify: link walker extracts backtick-wrapped markdown links
cleverhoods Jun 2, 2026
c46d0d0
cli: narrow target-token payload type in check command (mypy)
cleverhoods Jun 2, 2026
157fbb7
classify: rescue ambiguous lead verb demoted to nsubj by a parentheti…
cleverhoods Jun 3, 2026
78b7eca
classify: charge determiner-object frame for lexicon-absent lead verbs
cleverhoods Jun 3, 2026
e12802c
classify: mask parentheticals before late-constraint check so inner n…
cleverhoods Jun 3, 2026
8decb9c
build: consolidate ignore rules for non-shipped directories
cleverhoods Jun 6, 2026
d3f9d96
discovery: exclude dangling symlinks in ci_glob so ails check survive…
cleverhoods Jun 6, 2026
09586b8
rules: re-coordinate cursor import-depth-within-limit to CURSOR:S:000…
cleverhoods Jun 6, 2026
9ce1ff8
heal: backtick-wrap fixer skips markdown link labels and targets
cleverhoods Jun 6, 2026
3c68079
docs: align JSON output keys, score-delta claim, heal coverage, and f…
cleverhoods Jun 6, 2026
453a38d
cli: fix single-file check, add bare-noun targets, uniform command help
cleverhoods Jun 9, 2026
0e31a83
test: reformat duplicate-id comprehension to satisfy line-length linter
cleverhoods Jun 9, 2026
00492fa
fix(daemon,mcp): release resident models when idle (default-on idle u…
cleverhoods Jun 14, 2026
c5e7abd
feat(check): actionable findings — surfaced per-finding actions, serv…
cleverhoods Jun 15, 2026
3583814
fix(check,mcp): wall-clock backstop on check + uvx --refresh-package …
cleverhoods Jun 15, 2026
b7344b7
check: scope global subagent memory out of repo runs, fix on-demand t…
cleverhoods Jun 15, 2026
6e8719b
config: exclude hub/ inner repo from ails dogfood scan
cleverhoods Jun 15, 2026
2786e07
check: render per-group/per-file stats header when cwd differs from s…
cleverhoods Jun 15, 2026
caae9b2
test: xfail single-file-vs-whole-project comparison (dir-target reroo…
cleverhoods Jun 15, 2026
c245356
check: dedupe _is_external_pattern, require per_file_stats project_ro…
cleverhoods Jun 15, 2026
b298243
check: clear error for capability target on multi-agent repo (was sil…
cleverhoods Jun 15, 2026
a34a57f
check: stop mis-detecting inline `@code`/emails as @<path> imports (s…
cleverhoods Jun 15, 2026
7d01255
check: add exclude_files config key + --exclude-files flag for file-l…
cleverhoods Jun 15, 2026
a95656d
docs: correct exclude_files glob depth — segments match one component…
cleverhoods Jun 15, 2026
fe4fc45
registry: refresh agent capability matrix + configs vs current docs; …
cleverhoods Jun 15, 2026
97bbff3
Removing obsolete folders.
cleverhoods Jun 15, 2026
c184423
rules: direction-aware guidance for 4 core rules (constraints abstrac…
cleverhoods Jun 15, 2026
ff1c22a
rules: agent-aware instruction-size caps + eager-footprint CORE:E:000…
cleverhoods Jun 16, 2026
3176374
check: single quality score from the analysis service, folding comple…
cleverhoods Jun 16, 2026
25c6026
score: unscore zero-charged files + resolve structural rules per-agent
cleverhoods Jun 17, 2026
e79b690
docs: align score guide + getting-started to the single-score model
cleverhoods Jun 17, 2026
d302dda
check: suppress per-surface score bars on offline runs (no more not-s…
cleverhoods Jun 17, 2026
b5c0736
check: score @-imports as an Imported surface, panel-only for markdow…
cleverhoods Jun 17, 2026
3f1f7b8
check: render canonical rule IDs for client-side findings in text output
cleverhoods Jun 17, 2026
bf801d5
check: hyperlink rule IDs to docs pages, drop score-movers from the f…
cleverhoods Jun 17, 2026
e8f0e1d
mcp: key regime + surface health against the validated path, not the …
cleverhoods Jun 17, 2026
c402e3b
docs: correct faq score answer to the single-quality-score model
cleverhoods Jun 17, 2026
55fd784
check: route @-imports to the Imported surface in JSON output, matchi…
cleverhoods Jun 17, 2026
00ac2ca
docs: cover offline UX, leverage triage, Imported/Referenced, --fix a…
cleverhoods Jun 17, 2026
d6ea618
check: guard SIGALRM backstop on sys.platform so win32 mypy narrows (…
cleverhoods Jun 17, 2026
f1e7087
check: expose whole-project quality + level in combined JSON, action …
cleverhoods Jun 17, 2026
f972e16
docs: refresh README example to 0.5.11 output, target npx uvx refresh…
cleverhoods Jun 17, 2026
bd19a5e
docs: neutral stack/CDN examples and correct example rule id to CORE:…
cleverhoods Jun 18, 2026
d002f59
mapper+classify: describe lexicon/embedder comments by behavior, drop…
cleverhoods Jun 18, 2026
f845712
mcp: validate single-file target instead of returning No instruction …
cleverhoods Jun 18, 2026
2e38044
ci: create tag and GitHub release after PyPI and npm publish for idem…
cleverhoods Jun 18, 2026
eeea211
test: normalize capability-path rels with as_posix for Windows separa…
cleverhoods Jun 18, 2026
3fec0ce
test-action: match 0.5.11 offline behavior — score empty offline, min…
cleverhoods Jun 18, 2026
b03dbf0
test: make integration suite CI-robust — offline Quality headline, AN…
cleverhoods Jun 18, 2026
d4d0127
win: force UTF-8 stdout/stderr so md + scorecard glyphs do not crash …
cleverhoods Jun 18, 2026
6435df8
test: make single-file + subagent-memory checks Windows-robust (singl…
cleverhoods Jun 18, 2026
c00c44b
test: decode rules-help subprocess output as UTF-8 so cp1252 reader t…
cleverhoods Jun 18, 2026
b911d07
changelog: migrate 0.5.11 entries from UNRELEASED to CHANGELOG
cleverhoods Jun 18, 2026
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
3 changes: 1 addition & 2 deletions .ails/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ default_agent: claude
exclude_dirs:
- fixtures
- .venv
- docs
- specs
- hub
- framework
- .mypy_cache
- .ruff_cache
Expand Down
36 changes: 21 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,32 +152,18 @@ jobs:
shell: bash

publish:
name: Publish release
name: Publish to PyPI
needs: [check-release, verify-wheel]
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
environment: release
steps:
- uses: actions/checkout@v4

- uses: actions/download-artifact@v4
with:
name: dist
path: dist

- name: Create tag and GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${{ needs.check-release.outputs.version }}"
git tag "$VERSION"
git push origin "$VERSION"
gh release create "$VERSION" \
--title "v$VERSION" \
--generate-notes

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

Expand Down Expand Up @@ -209,3 +195,23 @@ jobs:
run: npm publish --access public --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

tag:
name: Tag and GitHub release
needs: [check-release, publish, npm]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4

- name: Create tag and GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${{ needs.check-release.outputs.version }}"
git tag "$VERSION"
git push origin "$VERSION"
gh release create "$VERSION" \
--title "v$VERSION" \
--generate-notes
26 changes: 18 additions & 8 deletions .github/workflows/test-action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ jobs:
echo "score=$SCORE"
echo "level=$LEVEL"
echo "violations=$VIOLATIONS"
[ -n "$SCORE" ] || { echo "FAIL: no score output"; exit 1; }
# score is intentionally empty on an offline run (no server score) — do not require it
[ -n "$LEVEL" ] || { echo "FAIL: no level output"; exit 1; }
[ -n "$VIOLATIONS" ] || { echo "FAIL: no violations output"; exit 1; }
[ -n "$RESULT" ] || { echo "FAIL: no result output"; exit 1; }

pass-explicit-agent:
Expand Down Expand Up @@ -68,22 +69,31 @@ jobs:

# --- Fail scenarios ---

fail-min-score:
name: "Fail: min-score gate rejects low score"
skip-min-score-offline:
name: "Pass: min-score gate skips on an offline run"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./action
id: should-fail
id: gate
continue-on-error: true
with:
min-score: "11"
from-source: "true"
- name: Verify failure
if: steps.should-fail.outcome != 'failure'
- name: Verify gate skipped (no server score offline)
env:
OUTCOME: ${{ steps.gate.outcome }}
SCORE: ${{ steps.gate.outputs.score }}
run: |
echo "Expected failure from min-score=11 but got: ${{ steps.should-fail.outcome }}"
exit 1
echo "outcome=$OUTCOME score=$SCORE"
# Offline there is no server score, so the min-score gate logs a warning and
# skips rather than failing the build — even an unreachable min-score=11 passes.
# The score-rejection path needs a live server score and is covered by unit tests.
if [ "$OUTCOME" = "failure" ]; then
echo "FAIL: min-score gate failed the build offline (should skip)"
exit 1
fi
[ -z "$SCORE" ] || { echo "FAIL: expected empty score on offline run, got $SCORE"; exit 1; }

fail-strict:
name: "Fail: strict mode on violations"
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ __pycache__
.pytest_cache
.ruff_cache

# Internal specs
specs/
# Internal coordination surface — private inner repo, never ships in the public CLI
/hub/

# Claude specific - Dev internals (root only; fixtures under tests/ and framework/ are tracked)
/.claude/
Expand Down
1 change: 1 addition & 0 deletions .ignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
# .ignore files with .gitignore syntax.
framework/rules/**/**/tests/**
tests/fixtures/**
/archive/
93 changes: 93 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

41 changes: 22 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Reporails CLI (v0.5.10)
# Reporails CLI (v0.5.11)

> **AI Instruction Diagnostics for coding agents. Validates the entire agentic instruction system against 120+ rules across six rule packs (core + per-agent). Supports Claude, Codex, Copilot, Cursor, and Gemini.**
>
Expand All @@ -12,36 +12,37 @@ npx @reporails/cli check
uvx --from reporails-cli ails check
```

No install, no account. Actionable findings in seconds - fix them, run again, watch the score improve:
No install, no account. The headline is a single **Quality** score (the analysis service's verdict on how well-formed your instructions are); fix the findings that move it, run again, watch it climb:

```
Reporails - Diagnostics
Reporails Diagnostics

┌─ Main (4) 61 directive / 9 constraint · 71% prose
┌─ Main (1) 10 directive / 1 constraint · 71% prose
│ CLAUDE.md 10 dir / 1 con / 1 amb · 71% prose
│ Missing tech stack declaration - list languages, frameworks, and runtimes CORE:C:0034
│ Missing MCP documentation - describe MCP server configuration if applicable CORE:C:0027
│ ... and 3 more
│ 4 brief · 1 orphan
│ ✗ Missing tech stack declaration — list languages, frameworks, runtimes CORE:C:0034
│ → Name the languages, frameworks, and runtimes the project targets.
│ ⚠ 'pytest' should be in backticks (×3) CORE:E:0003
│ → Wrap in backticks: `pytest`
│ ◦ +4 lower-priority (won't move your score yet) · -v to list
│ ⊕ 6 Pro diagnostics (1 error) — isolated instructions, buried directives
└─ 181 findings

[⋯ Agents (3) · Skills (10) · Rules (13) +318 findings ⋯]
└─ 12 findings

── Summary ────────────────────────────────────────────────────────

Score: 7.3 / 10 ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░ (3.9s)
Quality 6.4 / 10 ▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░ (4.1s)
Findings 3 errors · 38 warnings · 12 info
Agent: Claude
Level: L4 Delegated

Scope:
instructions: 277 directive / 448 prose (56%)
75 constraint / 10 ambiguous
instructions: 64 directive / 102 prose (61%)
18 constraint / 4 ambiguous

Main (4): ▓▓▓▓▓▓▓▓▓░░░░░ 6.9 Rules (13): ▓▓▓▓▓▓▓▓▓▓▓▓░░░ 7.9
Skills (10): ▓▓▓▓▓▓▓▓▓▓▓░░░░ 7.2 Agents (3): ▓▓▓▓▓▓▓▓▓▓░░░░░ 6.9
Main (1): ▓▓▓▓▓▓▓▓▓░░░░░ 6.4 1 err Rules (2): ▓▓▓▓▓▓▓▓▓▓▓▓░░░ 7.9
Skills (3): ▓▓▓▓▓▓▓▓▓▓▓░░░░ 7.2 Agents (1): ▓▓▓▓▓▓▓▓▓▓░░░░░ 6.6

499 findings · 5 errors · 416 warnings · 70 info
2 cross-file conflicts · 7 cross-file repetitions
+ 41 Pro diagnostics (1 error · 32 warnings) — sign in for line numbers + fix coordinates
```

## Install permanently
Expand Down Expand Up @@ -74,7 +75,7 @@ Run on every PR so instruction-quality regressions (contradictions, oversized fi
with:
api-key: ${{ secrets.REPORAILS_API_KEY }} # optional - sign-in for full diagnostic detail
strict: "true" # exit 1 if any rule fires
min-score: "7.0" # exit 1 if score < 7.0
min-score: "7.0" # exit 1 if Quality < 7.0
```

Capture your API key with `ails auth token` and store it as `REPORAILS_API_KEY` in your CI secret store. See [Configuration → Authentication](https://github.com/reporails/cli/blob/main/docs/configuration.md#authentication).
Expand All @@ -86,6 +87,8 @@ Capture your API key with `ails auth token` and store it as `REPORAILS_API_KEY`
- [Tiers and Limits](https://github.com/reporails/cli/blob/main/docs/tiers.md) - anonymous vs signed in, what each mode includes
- [Configuration](https://github.com/reporails/cli/blob/main/docs/configuration.md) - disabling rules, project / global config, exclude paths
- [Score Guide](https://github.com/reporails/cli/blob/main/docs/score-guide.md) - how the score is built and what it tells you
- [Capability Levels](https://github.com/reporails/cli/blob/main/docs/capability-levels.md) - the L0-L7 ladder and what each level requires
- [Rules CLI](https://github.com/reporails/cli/blob/main/docs/rules-cli.md) - `ails rules list --capability=skill` and friends — preflight rules before authoring
- [FAQ](https://github.com/reporails/cli/blob/main/docs/faq.md) - common questions

## Built and validated for
Expand Down
2 changes: 0 additions & 2 deletions UNRELEASED.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@

### Fixed

- [Lint]: Gate user-scope `~/...` rendering in mechanical-check attribution on the classifier's `precedence: user` property (read from agent config patterns) instead of path-prefix heuristics, so Windows tmp paths under the user profile no longer render with a `~/` prefix.

### Removed
10 changes: 7 additions & 3 deletions action/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,17 @@ runs:
run: |
python3 -c "
import sys
score = float('${{ steps.check.outputs.score }}')
raw = '${{ steps.check.outputs.score }}'.strip()
if not raw:
print('::warning::No quality score available (offline run) — skipping min-score gate')
sys.exit(0)
score = float(raw)
threshold = float('${{ inputs.min-score }}')
if score < threshold:
print(f'::error::Score {score:.1f} is below minimum threshold {threshold:.1f}')
print(f'::error::Quality {score:.1f} is below minimum threshold {threshold:.1f}')
sys.exit(1)
else:
print(f'Score {score:.1f} meets minimum threshold {threshold:.1f}')
print(f'Quality {score:.1f} meets minimum threshold {threshold:.1f}')
"

- name: Write step summary
Expand Down
28 changes: 11 additions & 17 deletions action/parse_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@

Usage: echo '<json>' | python3 parse_result.py
Outputs: _SCORE=X.X _LEVEL=LN _VIOLATIONS=N (one per line, eval-safe)

_SCORE is the analysis service's whole-project Quality verdict (the same number
`ails check` prints), read verbatim from the `quality` key — never recomputed here.
It is empty when no server score is available (offline run); the min-score gate
treats an empty score as "skip".
"""
from __future__ import annotations

Expand All @@ -15,26 +20,15 @@ def main() -> None:
files = d.get("files", {})
stats = d.get("stats", {})

n_findings = sum(f.get("count", 0) for f in files.values())
errors = stats.get("errors", 0)
warnings = stats.get("warnings", 0)
total = errors + warnings + stats.get("infos", 0)

if total == 0:
score = 10.0
else:
base = 6.0
denom = max(total, 1)
ep = min(4.0, errors / denom * 30)
wp = min(2.0, warnings / denom * 2)
score = max(0.0, min(10.0, base - ep - wp))
quality = d.get("quality") # float, or None when offline / no server score
level = d.get("level", "L0")
violations = stats.get("total_findings", sum(f.get("count", 0) for f in files.values()))

score = round(score, 1)
level = "L0" if not files else "L1"
score_out = "" if quality is None else f"{float(quality):.1f}"

print(f"_SCORE={score}")
print(f"_SCORE={score_out}")
print(f"_LEVEL={level}")
print(f"_VIOLATIONS={n_findings}")
print(f"_VIOLATIONS={violations}")


if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions docs/agent-support.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
title: "Agent Support"
description: "Which agents are recognized and what's covered"
version: "0.5.10"
last_updated: 2026-05-18
version: "0.5.11"
last_updated: 2026-06-06
---

# Agent Support
Expand Down
8 changes: 4 additions & 4 deletions docs/capability-levels.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
title: "Capability Levels"
description: "The ladder for where AI instructions live and how they act"
version: "0.5.10"
last_updated: 2026-05-19
version: "0.5.11"
last_updated: 2026-06-06
---

# Capability Levels
Expand Down Expand Up @@ -61,8 +61,8 @@ Each rung exists because the rung below it fails in a specific way. The trigger
| L5 | L6 | A constraint must hold 100% of the time, not 95% |
| L6 | L7 | You keep correcting the same preference across sessions |

Climbing without a symptom adds structure the model has to navigate without solving a problem you had. Under-climbing is more common: *"agent didn't run tests before pushing"* reads like a prompt-engineering problem but is usually a missing L6 hook; *"agent forgot we use Cloudflare Workers"* reads like context drift but is usually a missing L7 memory entry.
Climbing without a symptom adds structure the model has to navigate without solving a problem you had. Under-climbing is more common: *"agent didn't run tests before pushing"* reads like a prompt-engineering problem but is usually a missing L6 hook; *"agent forgot we use pnpm, not npm"* reads like context drift but is usually a missing L7 memory entry.

---

[← Score Guide](score-guide.md) · Capability Levels · [FAQ →](faq.md)
[← Rules CLI](rules-cli.md) · Capability Levels · [FAQ →](faq.md)
Loading
Loading