CNF compared every discovered settings.json/hooks.json pairwise regardless of
origin, so it treated installed plugins' bundled configs — each plugin's own
settings.json/hooks.json plus its shipped test fixtures and examples under
~/.claude/plugins/ — as if they were the user's authored cascade. A "conflict"
between two plugins' bundled test fixtures is not something a user can resolve,
yet these dominated the count: 339 CNF findings on this machine (315 high-sev
permission allow/deny "conflicts", 18 duplicate-hook, 6 settings-key), almost all
sourced from plugin-internal fixtures. The Conflicts grade was F on pure noise.
Fix: CNF excludes any file whose path is under `.claude/plugins/` from conflict
analysis (new isPluginBundled predicate; absPath marker). Kept CNF-local rather
than a discovery-level skip on purpose: an active plugin's contributed
hooks.json/.mcp.json legitimately lives in plugins/cache and other scanners need
it — only conflict analysis must ignore plugin-bundled files. Same class as
M-BUG-8 (non-live config trees treated as live). Suite 1344/0 (+3: plugin-bundled
exclusion, discovery-side sanity, over-exclusion guard). Frozen v5.0.0 + SC-5
snapshots untouched (marketplace-medium has no plugins/ paths), no re-seed.
Dogfood ~/.claude CNF 339->0 (F-grade was 100% plugin-bundled noise; the ~3
genuine user-scope local settings have no actual conflicting keys, matching the
plan C5 "real surface ~3 files" prediction).
Follow-up (not in this fix): classifyScope tags plugin-bundled files by checking
basePath instead of the file's own path, so scope:'plugin' is effectively dead
for a ~/.claude-rooted scan. Fixing it would let every scanner trust the scope
field, but that is a discovery-layer change beyond this bug's scope.
The DIS scanner collapsed Tool(param) rules to the bare tool name, so
Agent(model:opus) deny + Agent(model:sonnet) allow (and the same for
WebFetch(domain:...)) were flagged as dead config — a false positive now
that CC 2.1.178 matches Tool(param:value) and 2.1.172 adds domain rules.
The conflict-detector shared the blind spot from the other side: a
wildcard deny like WebFetch(domain:*) did not cover a
WebFetch(domain:good.com) allow, so a genuine cross-scope conflict was
missed (false negative).
New shared scanners/lib/permission-rules.mjs:
- parseRule / paramMatches (glob)
- dominates(deny, allow) -> DIS dead-allow (deny fully covers allow)
- rulesIntersect(a, b) -> CNF cross-scope conflict (match sets intersect)
DIS now delegates to dominates; conflict-detector :156 delegates to
rulesIntersect. A bare deny still covers all params, so true positives
are preserved (Bash deny + Bash(npm:*) allow still flagged).
Re-seeded the marketplace-medium snapshots: the false-positive CA-DIS
finding (Read(src/**) allow + Read(./.env) deny) is correctly gone. This
changes snapshot CONTENT only — envelope schema is unchanged, so --json
and --raw stay byte-stable.
Full suite: 837/837 green (+25). self-audit PASS, A(100)/A(97).
Add /ultraresearch-local for structured research combining local codebase
analysis with external knowledge via parallel agent swarms. Produces research
briefs with triangulation, confidence ratings, and source quality assessment.
New command: /ultraresearch-local with modes --quick, --local, --external, --fg.
New agents: research-orchestrator (opus), docs-researcher, community-researcher,
security-researcher, contrarian-researcher, gemini-bridge (all sonnet).
New template: research-brief-template.md.
Integration: --research flag in /ultraplan-local accepts pre-built research
briefs (up to 3), enriches the interview and exploration phases. Planning
orchestrator cross-references brief findings during synthesis.
Design principle: Context Engineering — right information to right agent at
right time. Research briefs are structured artifacts in the pipeline:
ultraresearch → brief → ultraplan --research → plan → ultraexecute.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>