Skip to content

Add rule engine Phases 6-7: docs, protocol, factory, release hardening#132

Merged
wrhalpin merged 1 commit intomainfrom
claude/create-gnat-admin-guide-BOSrp
Apr 19, 2026
Merged

Add rule engine Phases 6-7: docs, protocol, factory, release hardening#132
wrhalpin merged 1 commit intomainfrom
claude/create-gnat-admin-guide-BOSrp

Conversation

@wrhalpin
Copy link
Copy Markdown
Owner

Completes the analysis rule engine (WI-16 and WI-17):

  • RuleEngineProtocol (typing.Protocol) — structural contract for alternative engine implementations
  • create_engine() factory — reads [rules].engine from INI, currently supports "hy" only, clear error for unknown engines
  • 4 Diataxis docs:
    • tutorials/your-first-rule.md — end-to-end walkthrough
    • how-to/authoring-rules.md — 7 common patterns (promotion, refutation, blocking, AI ceiling, staleness, trust gate, annotation)
    • reference/rule-engine-spec.md — THE SPEC: rule format grammar, 26 helpers, evaluation model, audit model, policy parameters
    • explanation/rule-engine.md — architecture, Hy rationale, two-engine coexistence, advisor pattern, evidence resolution, AI ceiling design
  • CHANGELOG, CLAUDE.md, README.md updated with rule engine feature
  • All 17 work items from the implementation spec complete

https://claude.ai/code/session_01H5UbjsuiiGya5n1eUCxoaR

Completes the analysis rule engine (WI-16 and WI-17):

- RuleEngineProtocol (typing.Protocol) — structural contract for
  alternative engine implementations
- create_engine() factory — reads [rules].engine from INI, currently
  supports "hy" only, clear error for unknown engines
- 4 Diataxis docs:
  - tutorials/your-first-rule.md — end-to-end walkthrough
  - how-to/authoring-rules.md — 7 common patterns (promotion,
    refutation, blocking, AI ceiling, staleness, trust gate, annotation)
  - reference/rule-engine-spec.md — THE SPEC: rule format grammar,
    26 helpers, evaluation model, audit model, policy parameters
  - explanation/rule-engine.md — architecture, Hy rationale, two-engine
    coexistence, advisor pattern, evidence resolution, AI ceiling design
- CHANGELOG, CLAUDE.md, README.md updated with rule engine feature
- All 17 work items from the implementation spec complete

https://claude.ai/code/session_01H5UbjsuiiGya5n1eUCxoaR
Copilot AI review requested due to automatic review settings April 19, 2026 21:29
@wrhalpin wrhalpin merged commit 77cf101 into main Apr 19, 2026
3 of 20 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds the final “release hardening” layer around the Hy-based analysis rule engine by formalizing an engine interface, adding a config-driven engine factory, and publishing Diataxis documentation plus release notes/feature surfacing across top-level docs.

Changes:

  • Introduces RuleEngineProtocol (typing.Protocol) to define the structural contract for rule engines.
  • Adds create_engine() factory to construct a rule engine from [rules] INI config (currently supports hy only).
  • Adds full Diataxis documentation set (tutorial/how-to/reference/explanation) and updates README/CLAUDE/CHANGELOG to surface the feature.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
gnat/analysis/rules/protocol.py Adds a structural typing protocol for rule engine implementations.
gnat/analysis/rules/factory.py Adds INI-driven engine factory and unknown-engine validation.
docs/tutorials/your-first-rule.md New end-to-end tutorial for authoring and running a first rule.
docs/how-to/authoring-rules.md New recipe-style guide with common rule patterns and priority guidance.
docs/reference/rule-engine-spec.md New authoritative spec (rule grammar, helpers, evaluation/audit model, policy keys).
docs/explanation/rule-engine.md New design/architecture explanation and rationale for Hy + orchestration model.
README.md Surfaces the Rule Engine feature and the gnat[rules] extra.
CLAUDE.md Documents the new analysis/rules package and gnat[rules] extra.
CHANGELOG.md Adds Unreleased entry describing the analysis rule engine feature set.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +16 to +24
(defrule <rule-name>
:description <string> ;; optional
:phase <string> ;; required: "open", "supported", "refuted", "inconclusive"
:target-status <string> ;; optional: informational only
:priority <integer> ;; optional, default 50
:tags [<string> ...] ;; optional, default []
:when (fn [h ctx] <body>) ;; required: predicate returning truthy/falsy
:then (fn [h ctx] <body>)) ;; required: returns a Decision
```
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

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

This spec is internally inconsistent about :phase: the rule format section says it’s required and a string limited to specific status values, but later the “Phase gates” section says phase = None matches all statuses. Please clarify in one place (e.g., :phase is required but may be a status string or None).

Copilot uses AI. Check for mistakes.
Comment on lines +121 to +125
### Phase gates

A rule only evaluates if `hypothesis.status.value == rule.phase`. Rules
with `phase = None` match all statuses.

Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

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

The “Phase gates” section says phase = None matches all statuses, but earlier the grammar describes :phase as a required status string. Update one or both sections so the allowed values/behavior match the actual implementation and don’t confuse rule authors.

Copilot uses AI. Check for mistakes.

engine_name = "hy"
if hasattr(config, "get") and hasattr(config, "has_section") and config.has_section("rules"):
engine_name = config.get("rules", "engine", fallback="hy")
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

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

engine_name is read from config verbatim; values like "Hy"/" hy " will be rejected even though they’re semantically the same engine. Consider normalizing with .strip().lower() (similar to other config factories) before validating against _SUPPORTED_ENGINES.

Suggested change
engine_name = config.get("rules", "engine", fallback="hy")
engine_name = config.get("rules", "engine", fallback="hy").strip().lower()

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +44
if policy is None:
policy = RuleEnginePolicy.from_ini(config)

engine_name = "hy"
if hasattr(config, "get") and hasattr(config, "has_section") and config.has_section("rules"):
engine_name = config.get("rules", "engine", fallback="hy")
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

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

config is annotated as Any and guarded with hasattr, but RuleEnginePolicy.from_ini(config) requires a configparser.ConfigParser and will fail for other types (e.g., GNATConfig). Either narrow the parameter type to ConfigParser and drop the hasattr checks, or explicitly support GNATConfig by pulling config.parser before calling from_ini/get.

Suggested change
if policy is None:
policy = RuleEnginePolicy.from_ini(config)
engine_name = "hy"
if hasattr(config, "get") and hasattr(config, "has_section") and config.has_section("rules"):
engine_name = config.get("rules", "engine", fallback="hy")
ini_config = getattr(config, "parser", config)
if policy is None:
policy = RuleEnginePolicy.from_ini(ini_config)
engine_name = "hy"
if (
hasattr(ini_config, "get")
and hasattr(ini_config, "has_section")
and ini_config.has_section("rules")
):
engine_name = ini_config.get("rules", "engine", fallback="hy")

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +27
def create_engine(
config: Any,
policy: RuleEnginePolicy | None = None,
store: Any = None,
) -> AnalysisRuleEngine:
"""
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

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

The factory is intended to support multiple engine implementations via [rules].engine, but the return type is the concrete AnalysisRuleEngine. To keep callers decoupled from the implementation and align with RuleEngineProtocol, consider changing the return annotation to the protocol (or a narrow union) so adding another engine won’t force downstream type changes.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants