Skip to content

feat(components): auto-enforce core-module usage via init/SessionStart - #352

Merged
getappz merged 2 commits into
masterfrom
core-module-enforcement-components
Jul 28, 2026
Merged

feat(components): auto-enforce core-module usage via init/SessionStart#352
getappz merged 2 commits into
masterfrom
core-module-enforcement-components

Conversation

@getappz

@getappz getappz commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds a core-coaching component that seeds/refreshes 4 builtin coaching rules (usedocs, usesearch, useleanctx, usetsearch) nudging the flare gateway's docs/search/lean-ctx/tool-search wrappers over their native equivalents. Builtin tier is drift-protected across version bumps; a same-id rule the user has retagged override is left untouched.
  • Adds a gateway-permissions component that keeps ~/.claude/settings.json's permissions.allow containing the flare gateway tools (mcp__flare__docs, mcp__flare__search, mcp__flare__tool, ToolSearch) and strips superseded direct mcp__lean-ctx__* entries.
  • Both are needs_consent: false, so agentflare init and every SessionStart self-heal this setup — no separate wiring needed for "on update," since non-consent components already re-apply on every session start.
  • Widens coaching::store::list_rules to pub(crate) so components.rs can inspect existing rule state before deciding to seed/refresh.

Test plan

  • cargo build --bin agentflare — clean
  • cargo clippy --bin agentflare — clean
  • cargo test --bin agentflare -- components:: coaching:: — 71/71 pass, including 8 new tests covering idempotency, override-preservation, fresh-seed, and non-claude-code host gating
  • Verified live: rebuilt the release binary, ran agentflare init --agent claude-code — both new components report satisfied against real local state

Summary by CodeRabbit

  • New Features
    • Automatically ensures built-in coaching rules are seeded and refreshed for Claude Code during initialization and session start.
    • Automatically manages Claude gateway permissions, including removing outdated direct permissions.
    • Provides clear success/failure feedback for these setup actions and keeps operations idempotent.
    • Preserves any user-defined coaching rule overrides while updating only built-in defaults.
  • Bug Fixes
    • Prevents configuration drift by syncing missing or changed built-in coaching settings and gateway permissions without overwriting user choices.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

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: CHILL

Plan: Pro Plus

Run ID: e262f8ab-a868-4cd0-b0b4-115b1d22442d

📥 Commits

Reviewing files that changed from the base of the PR and between 4257610 and 2d4b596.

📒 Files selected for processing (3)
  • src/coaching/mod.rs
  • src/coaching/store.rs
  • src/components.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/coaching/store.rs
  • src/coaching/mod.rs
  • src/components.rs

📝 Walkthrough

Walkthrough

Claude Code component management now seeds and refreshes built-in coaching rules, preserves user overrides, and synchronizes gateway permissions in Claude settings. Coaching rule visibility is widened crate-wide, with component lifecycle and idempotency tests added.

Changes

Coaching and gateway synchronization

Layer / File(s) Summary
Core coaching defaults
src/coaching/mod.rs, src/coaching/store.rs, src/components.rs
The rule listing API is exposed crate-wide, while default Builtin-tier coaching rules are checked, seeded, and refreshed without replacing user overrides.
Gateway permission synchronization
src/components.rs
Required Claude gateway allow entries are added, stale direct lean-ctx permissions are removed, and settings are written only when changed.
Component lifecycle validation
src/components.rs
The new components are registered for Claude Code, with lifecycle, ordering, idempotency, malformed-permission, seeding, and override-preservation tests.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ClaudeCode as Claude Code
  participant Components as get_components()
  participant Coaching as core-coaching
  participant Gateway as gateway-permissions
  participant Settings as ~/.claude/settings.json
  ClaudeCode->>Components: discover components
  Components->>Coaching: check and apply coaching defaults
  Coaching->>Coaching: preserve Override-tier rules
  Components->>Gateway: check and apply gateway permissions
  Gateway->>Settings: synchronize allow entries
  Settings-->>Gateway: changed or unchanged result
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: automatic enforcement during init and SessionStart for core-module behavior.
Description check ✅ Passed The description covers the summary and test plan with concrete results; only the reviewer notes section is missing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 core-module-enforcement-components

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: 2

🧹 Nitpick comments (4)
src/components.rs (3)

1560-1570: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add the symmetric non-claude-code check for core-coaching.

gateway-permissions has this coverage, but nothing asserts core-coaching's !claude_code_only short-circuit.

💚 Suggested test
#[test]
fn core_coaching_is_satisfied_for_non_claude_code_hosts() {
    crate::paths::test_support::with_temp_home(|| {
        let components = get_components("opencode");
        let cc = components.iter().find(|c| c.id == "core-coaching").unwrap();
        assert!((cc.check)());
    });
}
🤖 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/components.rs` around lines 1560 - 1570, Add a symmetric test beside
gateway_permissions_is_satisfied_for_non_claude_code_hosts for the core-coaching
component, using get_components("opencode"), locating the "core-coaching" entry,
and asserting its check returns true within with_temp_home.

887-898: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Ordering: core-coaching seeds after the rules component runs.

rule_targets() includes coaching-sourced rules, but rules is applied earlier in this vector, so on a fresh install the four freshly-seeded rules aren't materialized into the host rule files until the next init/SessionStart. Self-healing, but a one-session delay. Consider placing core-coaching before rules if that write ordering matters.

🤖 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/components.rs` around lines 887 - 898, The component vector currently
applies rules before core-coaching seeds its coaching rules, causing newly
seeded rules to materialize only on the next run. Reorder the component
declarations so core-coaching appears before rules, while preserving both
components’ existing checks and apply behavior.

424-433: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Drift detection only compares body.

title, tools, and sync changes in DEFAULT_COACHING_RULES won't trigger a refresh on existing installs, so a future edit to those fields silently never propagates. Consider comparing the trigger/sync/title too if those are expected to evolve.

🤖 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/components.rs` around lines 424 - 433, Update coaching_defaults_satisfied
to compare every default rule field that should propagate, including title,
tools/triggers, sync, and body, rather than only body. Preserve the existing
Override-tier behavior and missing-rule handling, while ensuring changes to
these fields make the defaults unsatisfied and trigger refresh.
src/coaching/mod.rs (1)

23-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Comment sits below the item it documents and names rules that don't exist.

The trailing comment describes the list_rules re-export above it, so it reads as documenting the next item. It also refers to flare-docs, flare-search, lean-ctx, tool-search, while the actual rule ids in src/components.rs are usedocs, usesearch, useleanctx, usetsearch.

♻️ Suggested reorder/wording
-pub(crate) use store::list_rules;
-
-// Also used by components.rs to seed/refresh the built-in core-module
-// coaching rules (flare-docs, flare-search, lean-ctx, tool-search) on every
-// `agentflare init` and SessionStart.
+// Also used by components.rs to seed/refresh the built-in core-module
+// coaching rules (usedocs, usesearch, useleanctx, usetsearch) on every
+// `agentflare init` and SessionStart.
+pub(crate) use store::list_rules;
🤖 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/coaching/mod.rs` around lines 23 - 27, Move the comment above the
pub(crate) use store::list_rules re-export so it documents that symbol, and
update the listed built-in rule IDs to usedocs, usesearch, useleanctx, and
usetsearch, matching the definitions in components.rs.
🤖 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/components.rs`:
- Around line 497-511: Update the permissions handling in the settings flow to
return an error when the existing permissions value is not an object, matching
the existing permissions.allow validation. Remove the branch that replaces
malformed permissions with an empty object, while preserving creation of an
empty object only when the permissions key is absent.
- Around line 438-473: The apply_coaching_defaults function must surface
failures from coaching::apply_rule instead of discarding them via is_ok(). Track
or report the returned error when any default rule cannot be applied, and ensure
the result is not reported as “already up to date” when seeding fails, so
repeated coaching_defaults_satisfied() failures provide a diagnostic.

---

Nitpick comments:
In `@src/coaching/mod.rs`:
- Around line 23-27: Move the comment above the pub(crate) use store::list_rules
re-export so it documents that symbol, and update the listed built-in rule IDs
to usedocs, usesearch, useleanctx, and usetsearch, matching the definitions in
components.rs.

In `@src/components.rs`:
- Around line 1560-1570: Add a symmetric test beside
gateway_permissions_is_satisfied_for_non_claude_code_hosts for the core-coaching
component, using get_components("opencode"), locating the "core-coaching" entry,
and asserting its check returns true within with_temp_home.
- Around line 887-898: The component vector currently applies rules before
core-coaching seeds its coaching rules, causing newly seeded rules to
materialize only on the next run. Reorder the component declarations so
core-coaching appears before rules, while preserving both components’ existing
checks and apply behavior.
- Around line 424-433: Update coaching_defaults_satisfied to compare every
default rule field that should propagate, including title, tools/triggers, sync,
and body, rather than only body. Preserve the existing Override-tier behavior
and missing-rule handling, while ensuring changes to these fields make the
defaults unsatisfied and trigger refresh.
🪄 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: CHILL

Plan: Pro Plus

Run ID: 47d41d0f-59fc-4940-a08d-e3de7201076a

📥 Commits

Reviewing files that changed from the base of the PR and between 527a4c8 and 4257610.

📒 Files selected for processing (3)
  • src/coaching/mod.rs
  • src/coaching/store.rs
  • src/components.rs

Comment thread src/components.rs
Comment thread src/components.rs
shiva and others added 2 commits July 28, 2026 08:37
Adds two non-consent components so agentflare init and every SessionStart
self-heal the setup needed to actually use flare-docs, flare-search, and
lean-ctx through the gateway, instead of relying on hand-run CLI commands:

- core-coaching: seeds/refreshes 4 builtin coaching rules (usedocs,
  usesearch, useleanctx, usetsearch) that nudge the flare gateway's
  docs/search/lean-ctx/tool-search wrappers over their native equivalents.
  Drift-protected across version bumps; a same-id rule the user has
  overridden to a different tier is left alone.
- gateway-permissions: keeps ~/.claude/settings.json's permissions.allow
  containing the flare gateway tools (mcp__flare__docs, mcp__flare__search,
  mcp__flare__tool, ToolSearch) and strips superseded direct
  mcp__lean-ctx__* entries.

Also widens coaching::store::list_rules to pub(crate) so components.rs can
read existing rule state when deciding whether to seed or refresh.
…ings, seed before rules

apply_rule errors were dropped by is_ok(), so a failed seed reported
"already up to date" while the check kept failing every SessionStart with
no diagnostic. A malformed permissions value was replaced with {} and
written back, unlike permissions.allow which errors out. core-coaching
also ran after rules, whose write_if_absent had already written the host
rule files without the four seeded rules -- and whose check then passes
forever, so nothing picked them up later.
@getappz
getappz force-pushed the core-module-enforcement-components branch from 4257610 to 2d4b596 Compare July 28, 2026 03:07
@getappz

getappz commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Review pass — three findings fixed in 2d4b596, plus cargo fmt and a rebase onto master.

1. Failed apply_rule calls reported as success (major)

.is_ok() dropped the error, so a failed seed printed "already up to date" while coaching_defaults_satisfied() kept returning false on every SessionStart with nothing explaining why.

     let mut changed = vec![];
+    let mut failed = vec![];
     for d in DEFAULT_COACHING_RULES {
@@
-        if crate::coaching::apply_rule(
-            d.id, d.title, d.body, Some(trigger),
-            crate::coaching::rule::RuleTier::Builtin, sync,
-        )
-        .is_ok()
-        {
-            changed.push(d.id);
+        match crate::coaching::apply_rule(
+            d.id, d.title, d.body, Some(trigger),
+            crate::coaching::rule::RuleTier::Builtin, sync,
+        ) {
+            Ok(_) => changed.push(d.id),
+            Err(e) => failed.push(format!("{}: {e}", d.id)),
         }
     }
-    if changed.is_empty() {
+    if !failed.is_empty() {
+        format!(
+            "core-module coaching rules failed: {} (seeded/refreshed: {})",
+            failed.join("; "),
+            if changed.is_empty() { "none".to_string() } else { changed.join(", ") }
+        )
+    } else if changed.is_empty() {
         "core-module coaching rules already up to date".to_string()

2. Malformed permissions silently clobbered (minor)

A non-object permissions was replaced with {} and written back, losing whatever the user had — while a non-array permissions.allow correctly errors and leaves the file alone. Now both error.

     let permissions = obj
         .entry("permissions")
         .or_insert_with(|| serde_json::json!({}));
-    if !permissions.is_object() {
-        *permissions = serde_json::json!({});
-    }
     let perm_obj = permissions
         .as_object_mut()
         .ok_or("permissions is not an object")?;

Covered by apply_gateway_permissions_errors_on_malformed_permissions_rather_than_clobbering.

3. core-coaching ordered after rules — the seeded rules never reached the rule files

CodeRabbit read this as a self-healing one-session delay. It isn't. rules's apply uses write_if_absent, and its check is targets.iter().all(|(p, _)| p.exists()) — once the files exist the component is satisfied forever and never rewrites them. So on a fresh install rules wrote the host rule files before the four defaults were seeded, and no later pass ever folded them in. (The hook path still delivers them via rule_bodies_for_tool, so this was a rule-file gap, not a total miss.)

Fixed by moving the component ahead of rules in the vector:

     let mut components = vec![
+        // Ahead of `rules` on purpose: `rule_targets` folds coaching-sourced
+        // rules into the host rule files, and `rules` writes each file only
+        // when absent. Seeded after it, these four would miss the write and
+        // never make it in -- `rules`'s check passes once the files exist, so
+        // there is no later pass to pick them up.
+        Component {
+            id: "core-coaching",
+            ...
+        },
         Component {
             id: "rules",

Locked in by core_coaching_is_ordered_before_the_rules_component, and the two component-order assertions were updated to match.

Also

  • Added coaching_defaults_are_satisfied_for_non_claude_code_hosts (the symmetric non-claude-code check CodeRabbit noted was missing for core-coaching).
  • cargo fmt --all — this is what the red fmt job was reporting.

Verified locally: cargo fmt --all --check clean, cargo clippy -p agentflare --all-targets --all-features clean, cargo test --workspace 845 passed / 0 failed.

@getappz
getappz merged commit 8458e0d into master Jul 28, 2026
16 checks passed
@getappz
getappz deleted the core-module-enforcement-components branch July 28, 2026 03:15
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.

1 participant