Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
7ebddf1
Surface server reset_in as 'Try again in ~N min.' in rate_limit_excee…
cleverhoods May 9, 2026
b455a04
Stop tracking root CLAUDE.md (dev-internal, gitignored since 878b6c4)
cleverhoods May 10, 2026
27293a0
Fix frontmatter_valid_glob crash on comma-separated paths: values
cleverhoods May 11, 2026
e4e91a2
Dedupe symlinked instruction files at discovery; render duplicates wi…
cleverhoods May 11, 2026
b54a175
Untrack .ails/backbone.yml; gitignore for dev-internal use
cleverhoods May 11, 2026
2d5bb2e
Add specs coverage + drift validation scripts with poe tasks
cleverhoods May 11, 2026
bff489b
Expand pytest marker taxonomy + add check_test_markers.py auditor
cleverhoods May 11, 2026
e2d0357
Genericize requires_model marker description (drop ML stack internals)
cleverhoods May 11, 2026
9f4bbc6
Tag 955 tests with lane + subsystem markers; enforce in qa_fast
cleverhoods May 11, 2026
a2244b1
Bootstrap core/platform/ skeleton + report-only architecture tests
cleverhoods May 11, 2026
2a9c69d
Phase 2: relocate {analytics,bootstrap,config,utils} into core/platform/
cleverhoods May 11, 2026
f302a59
Phase 3: relocate {models,results} to dto/ and {applicability,levels}…
cleverhoods May 11, 2026
8177637
Phase 4: relocate {api_client,payload,registry,rule_builder} to adapt…
cleverhoods May 11, 2026
54de06f
Phase 5: flip architecture tests to fail mode with documented allowlist
cleverhoods May 11, 2026
b17b7c1
Phase 6: consolidate caching subsystem into core/cache/ subpackage
cleverhoods May 11, 2026
831fd8b
Phase 6: consolidate {funnel,classify,heal,discovery,lint} subsystems…
cleverhoods May 11, 2026
5371e47
Phase 7: extract RulesetMap and wire-format dataclasses to core/platf…
cleverhoods May 11, 2026
dccd545
Funnel: render CTA + bug-report URLs as OSC 8 hyperlinks with short l…
cleverhoods May 11, 2026
44a4c7f
Funnel: silence pre-report 403 warnings; honest 'returned HTTP N' CTA…
cleverhoods May 11, 2026
78527ae
API client: send User-Agent: reporails-cli/<version> to bypass Cloudf…
cleverhoods May 11, 2026
a3e802a
Tests: add TestOutgoingHeaders to prevent silent User-Agent regression
cleverhoods May 11, 2026
35bc4ba
UNRELEASED: genericize the 403 root-cause description (no infra naming)
cleverhoods May 11, 2026
24b410a
Split filesystem detection out of `policy/applicability.py` into `dis…
cleverhoods May 11, 2026
d11e346
Relocate remaining `core/*.py` files into the substrate and new `core…
cleverhoods May 11, 2026
7b61ae5
Drop legacy hardcoded "recommended" rule-pack feature; user packs con…
cleverhoods May 11, 2026
84160b4
Mapper split (Stage 0): extract `expand_imports` from `mapper.py` to …
cleverhoods May 11, 2026
18d67e9
Mapper split (singleton): extract `Models` + `get_models` from `mappe…
cleverhoods May 11, 2026
553b5dc
Mapper split (Stage 4): extract `check_specificity` + regex constants…
cleverhoods May 11, 2026
89bad97
Mapper split (Stage 3): extract `classify_charge` + three-phase class…
cleverhoods May 11, 2026
3124add
Mapper split (Stages 1+2): extract `tokenize` + AST walker (~800 line…
cleverhoods May 11, 2026
5fae078
Mapper split (Stage 6): extract `cluster_topics` + clustering helpers…
cleverhoods May 11, 2026
1849ff3
Mapper split (Stage 5): extract `_embed_atoms_deduped` + ONNX helpers…
cleverhoods May 11, 2026
367bace
Mapper split (serialize): extract `save_ruleset_map` + `load_ruleset_…
cleverhoods May 11, 2026
e7f48d2
Build: pin `requires-python` to `>=3.12,<3.14` to dodge Python 3.14 `…
cleverhoods May 11, 2026
33497de
Mapper split (inspect): extract frontmatter + agent-registry helpers …
cleverhoods May 11, 2026
a161a67
Build: pin `en_core_web_sm` spaCy model as a direct runtime dependenc…
cleverhoods May 11, 2026
99c7d0b
Remove the hidden `ails map` command and its dead support code (`gene…
cleverhoods May 11, 2026
3d825f9
Mapper split (orchestrator): rename `core/mapper/mapper.py` to `core/…
cleverhoods May 11, 2026
298e4ef
Mapper split (Stage 7): extract `build_ruleset_map` from `pipeline.py…
cleverhoods May 11, 2026
9f1d423
Bundle `en_core_web_sm` spaCy pipeline in wheel; drop URL dep that bl…
cleverhoods May 11, 2026
66b525c
Release 0.5.9
cleverhoods May 11, 2026
6b76ae2
Release 0.5.9: README accuracy + compact CHANGELOG
cleverhoods May 12, 2026
d28a5cd
Fix `pre-release-check.sh` wheel-install step: pin the test venv to p…
cleverhoods May 12, 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
53 changes: 0 additions & 53 deletions .ails/backbone.yml

This file was deleted.

10 changes: 8 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@ specs/
/.mcp.json
/CLAUDE.md

# Local project backbone (developer-private; documents internal modules,
# specs paths, and toolchain that should not ship in a public repo)
/.ails/backbone.yml

# Pre-existing local rule-harness scaffolds with the wrong filename for CORE
# rules (should be AGENTS.md — agent-agnostic). 0.5.6 will rename these and
# track them properly. Until then they live local-only.
framework/rules/**/tests/**/CLAUDE.md
framework/rules/**/tests/**/.claude/

# Bundled ONNX embedding model — fetched by scripts/fetch_bundled_model.py
# (dev-only, not committed; populated on clone and in CI before hatch build)
# Bundled ML assets — fetched by scripts/fetch_bundled_model.py
# (dev-only, not committed; populated on clone and in CI before hatch build).
# ONNX embedder (~80 MB) + spaCy en_core_web_sm pipeline (~15 MB).
src/reporails_cli/bundled/models/
src/reporails_cli/bundled/spacy/

# npm packaging — README.md is copied from the repo root by the prepack
# script in packages/npm/package.json before `npm pack` / `npm publish`.
Expand Down
2 changes: 1 addition & 1 deletion .ignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Prevent Claude Code from discovering .claude directories inside test fixtures
# as real configuration. Ripgrep (used by Claude for file discovery) respects
# .ignore files with .gitignore syntax.
framework/rules/**/tests/**
framework/rules/**/**/tests/**
tests/fixtures/**
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
# Changelog

## 0.5.9

### Added

- Tooling: `uv run poe specs_check` validates internal subsystem coverage (declared subsystems exist, each spec is within line-budget, modules colocate under one subpackage); `uv run poe spec_drift` flags potentially stale design docs whose source has been edited more recently
- Tooling: expanded `pytest` marker taxonomy in `pyproject.toml` for granular test selection (lane, cost, subsystem) with new poe tasks `test_fast`, `test_arch`, `test_contracts`, `test_markers`
- Tooling: every `tests/*` test function now carries pytest lane (`unit`/`integration`/`e2e`) + subsystem (`subsys_*`) markers; `check_test_markers.py` enforces tagging on every `qa_fast` run, enabling `pytest -m subsys_caching` and similar slicing
- Tooling: hexagonal platform substrate skeleton bootstrapped at `core/platform/{contract,dto,policy,adapters,runtime,config,observability,utils}` with report-only architecture tests guarding pure-layer purity and adapter boundary (`tests/unit/architecture/`)

### Changed

- Build: bundle the `en_core_web_sm` spaCy pipeline (~15 MB) inside the wheel under `bundled/spacy/`, alongside the existing bundled ONNX embedder. `core/mapper/models.py` loads the pipeline by local filesystem path. End users no longer need a separate model download — `pip install reporails-cli` (or `uv pip install`, or `npx @reporails/cli`) delivers the full model bundle.
- Build: tightened `requires-python` to `>=3.12,<3.14`; Python 3.14 ships a `pydantic.v1` introspection regression that breaks `import spacy`. The CLI's verb-lexicon fallback covered the failure silently but with reduced precision. The pin restores spaCy classification under `uv sync`.
- API client: outgoing diagnostic requests now carry a `User-Agent: reporails-cli/<version>` header for accurate attribution in server-side logs; previously the generic `python-httpx/<version>` default was sent.
- Funnel: rate-limit CTA surfaces a "Try again in ~N min." hint when the server returns `reset_in`, between the limit blurb and the upgrade prompt.
- Funnel: CTA and bug-report URLs render as OSC 8 terminal hyperlinks with a short clickable label (`github.com/reporails/cli/issues/new`) instead of dumping the full percent-encoded prefilled URL; falls back to the short label on terminals without hyperlink support.
- Funnel: demoted the "Could not parse N response body" and "Server returned N for tier=" stderr warnings to debug logging so they no longer print above the diagnostic report; reworded the `unknown_error` CTA to `Diagnostics server returned HTTP <code>`.
- Display: file rows annotate duplicates with `(+alias)` labels — symlinked surfaces show the differing path component (e.g. `mintlify (+.claude)`), same-directory content-identical pairs show the alternate filename (e.g. `AGENTS.md (+CLAUDE.md)`).
- Internals: hexagonal platform substrate consolidated under `core/platform/{contract,dto,policy,adapters,runtime,config,observability,utils}`. Every top-level `core/*.py` moved into its appropriate layer (DTOs, adapters, runtime, etc.), with a new `core/install/` subsystem for installer-related modules. Architecture tests at `tests/unit/architecture/` run in fail mode — any forbidden cross-layer import blocks the build.
- Internals: five subsystems consolidated into named subpackages — `core/cache/`, `core/funnel/`, `core/classify/`, `core/heal/`, `core/discovery/`, `core/lint/` — each matching its design boundary.
- Internals: the mapper subsystem went the furthest. `core/mapper/mapper.py` was split into one module per pipeline stage (`imports.py`, `parse.py`, `classify.py`, `annotate.py`, `embed.py`, `cluster.py`, `assemble.py`) plus shared `models.py`, `serialize.py`, `inspect.py`. The orchestration spine retains the name `core/mapper/pipeline.py`. Public import surface (`map_ruleset`, `content_hash`, `map_file`) is unchanged; callers now import via the `core.mapper` package facade.
- Internals: removed the legacy "recommended" rules-overlay machinery from `ails config set/get/list`, `GlobalConfig`/`ProjectConfig`, and `core/install/`. User-installed rule packages remain supported through the generic `packages: [...]` mechanism in `.ails/config.yml` (clone any rule pack into `.ails/packages/<name>/` or `~/.reporails/packages/<name>/`).

### Fixed

- Check: `frontmatter_valid_glob` no longer crashes on comma-separated `paths:` values; each entry is now split and validated individually, and invalid glob syntax surfaces as a structured check failure instead of an unhandled exception
- Discovery: skill and rule files that appear under multiple agent surfaces via symlinks (e.g. `.claude/skills/` → `.agents/skills/`) are now collapsed to one canonical entry, eliminating duplicate findings and inflated scoring

### Removed

- CLI: removed `ails map`.

## 0.5.8

### Added
Expand Down
56 changes: 0 additions & 56 deletions CLAUDE.md

This file was deleted.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Reporails CLI (v0.5.8)
# Reporails CLI (v0.5.9)

> **AI Instruction Diagnostics for coding agents. Validates the entire agentic instruction system against 92+ rules across six categories. Supports Claude, Codex, Copilot, Cursor, and Gemini.**
> **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.**
>
> *Beta phase - moving fast, feedback welcome.*

Expand Down
2 changes: 1 addition & 1 deletion framework/rules/core/heading-as-instruction/rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ Use the heading as a section label and put the instruction in the first line of

## Limitations

Detects charged heading atoms (headings classified as directive, imperative, or constraint by the charge classifier). Short headings with common verbs may be false positives — "## Process" is a label, not an instruction, but contains a verb.
Detects headings classified as directive, imperative, or constraint. Short headings with common verbs may be false positives — "## Process" is a label, not an instruction, but contains a verb.
13 changes: 12 additions & 1 deletion hatch_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
"framework/sources.yml": "reporails_cli/sources.yml",
}

# Bundled model (gitignored, but must be in wheel).
# Bundled ML assets (gitignored, but must be in wheel).
# Populated by scripts/fetch_bundled_model.py before build.
BUNDLED_MODEL = "src/reporails_cli/bundled/models"
BUNDLED_MODEL_DEST = "reporails_cli/bundled/models"
BUNDLED_SPACY = "src/reporails_cli/bundled/spacy"
BUNDLED_SPACY_DEST = "reporails_cli/bundled/spacy"

# Directory names to skip when bundling (rule test fixtures are dev-only)
SKIP_DIRS = {"tests"}
Expand Down Expand Up @@ -53,6 +55,15 @@ def initialize(self, version: str, build_data: dict[str, Any]) -> None:
rel = path.relative_to(root / "src")
force_include[str(path)] = str(rel)

# Bundle spaCy en_core_web_sm model (gitignored but required at runtime)
spacy_dir = root / BUNDLED_SPACY
if spacy_dir.is_dir():
for path in spacy_dir.rglob("*"):
if not path.is_file():
continue
rel = path.relative_to(root / "src")
force_include[str(path)] = str(rel)

# Remove the pyproject.toml force-include entries (they'd double-include)
# — handled by clearing them from config before this hook, or by
# removing them from pyproject.toml entirely.
Expand Down
2 changes: 1 addition & 1 deletion packages/npm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@reporails/cli",
"version": "0.5.8",
"version": "0.5.9",
"description": "AI instruction diagnostics for coding agents",
"type": "module",
"bin": {
Expand Down
50 changes: 39 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[project]
name = "reporails-cli"
version = "0.5.8"
version = "0.5.9"
description = "AI instruction diagnostics for coding agents"
readme = "README.md"
license = "BUSL-1.1"
requires-python = ">=3.12"
requires-python = ">=3.12,<3.14"
authors = [{ name = "Reporails Team" }]
keywords = ["ai-instructions", "claude", "codex", "copilot", "cursor", "gemini", "diagnostics", "validation", "mcp"]
classifiers = [
Expand Down Expand Up @@ -34,9 +34,11 @@ dependencies = [
"onnxruntime>=1.18,<2",
"tokenizers>=0.19,<1",
"numpy>=1.26,<3",
# spaCy drives Phase 3 imperative classification in the mapper. It's a
# hard requirement — the fallback verb lexicon drops charge accuracy
# from 96% to ~78%. With the torch blocker active, spaCy loads in ~1s.
# spaCy provides the dependency-parser branch of the classifier. With
# the torch blocker active, spaCy loads in ~1s. The en_core_web_sm
# pipeline files ship inside the wheel under bundled/spacy/ rather
# than as a separate runtime dep — spaCy does not publish its language
# models on PyPI, so a direct-URL pin would block PyPI publication.
"spacy>=3.8.11,<4",
# sklearn for topic clustering in mapper
"scikit-learn>=1.4.0",
Expand Down Expand Up @@ -91,8 +93,6 @@ Issues = "https://github.com/reporails/cli/issues"
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.metadata]

[tool.hatch.build.targets.wheel]
packages = ["src/reporails_cli"]
artifacts = [
Expand All @@ -110,10 +110,16 @@ type = "mypy src/"
test_unit = "pytest tests/unit/ -v"
test_integration = "pytest tests/integration/ -v"
test_smoke = "pytest tests/smoke/ -v -m e2e"
qa_fast = ["fmt", "lint_fix", "lint", "pylint_struct", "type", "test_unit"]
qa_fast = ["fmt", "lint_fix", "lint", "pylint_struct", "type", "test_markers", "test_unit"]
qa = ["fmt", "lint_fix", "lint", "pylint_struct", "type", "test_unit", "test_integration", "test_smoke"]
mcp_dev = "python -m reporails_cli.interfaces.mcp.server"
fetch_bundled_model = "python scripts/fetch_bundled_model.py"
specs_check = "python scripts/specs_check.py"
spec_drift = "python scripts/check_spec_drift.py"
test_markers = "python scripts/check_test_markers.py"
test_fast = "pytest -m 'unit and not slow and not requires_model'"
test_arch = "pytest -m architecture"
test_contracts = "pytest -m contract"

[tool.ruff]
target-version = "py312"
Expand Down Expand Up @@ -171,7 +177,29 @@ max-module-lines = 600
testpaths = ["tests"]
addopts = "--strict-markers"
markers = [
"unit: Fast, isolated unit tests",
"integration: Cross-component tests",
"e2e: End-to-end tests",
# Lane — mutually exclusive within a test
"unit: Fast, isolated, no I/O outside repo, no ML models",
"integration: Cross-component, may load ML models",
"e2e: End-to-end CLI on fixture project",
"smoke: Alias for e2e (preferred for new tests)",
"architecture: AST/structural assertions across the codebase",
"contract: Pure validator function in isolation",
# Cost — orthogonal, additive
"slow: Takes more than 1 second",
"requires_model: Needs the bundled ML runtime",
"requires_network: Hits real external service",
"real_deps: Needs API key or live backend",
# Subsystem tags — one or more per test
"subsys_caching",
"subsys_map",
"subsys_classify",
"subsys_runtime",
"subsys_heal",
"subsys_funnel",
"subsys_lint",
"subsys_diagnostic",
"subsys_server",
"subsys_api",
"subsys_gates",
"subsys_cli_ux",
]
Loading
Loading