## File: docs/architecture/pack-design.md

# Pack Architecture Decisions

This document captures the architectural decisions required before expanding
pack coverage. It is the canonical reference for pack authors and reviewers.

## 1) REST API Pattern Detection (curl/httpie)

**Decision**: Hybrid detection (keyword gate + lightweight argument parsing).

- **Gate** on `curl`, `http`, or `httpie` keywords to avoid touching every command.
- **Parse** method flags (`-X`, `--request`, `--method`) and extract the URL.
- **Pack-specific host filters**: each pack owns its hostnames or URL prefixes.
- **Decision rule**: only treat `DELETE` (and other explicitly destructive methods)
  as destructive when the host/path match a pack.

**Rationale**: A pure keyword scan is too noisy; full AST parsing is too slow.
The hybrid approach is fast, deterministic, and maintainable.

## 2) Pack Overlap Resolution

**Decision**: Keep packs separate but clarify boundaries.

From `git_safety_guard-qdhh`: `cicd.github_actions` is for CI operations
(secrets, variables, workflows, runs). `platform.github` is for broader platform
operations (repos, releases, deploy keys, webhooks, collaborators). Do not
duplicate patterns across packs; document scope in pack descriptions.

**Rationale**: Separation reduces regex scope and allows users to enable only
what they need. Clear boundaries avoid duplication and false positives.

## 3) Command Alias Handling

**Decision**: Explicit aliases for high-impact, common tools plus opt-in config.

- Include **well-known aliases** in keywords (e.g., `k` for `kubectl`).
- Avoid regex alias heuristics that cause false positives.
- Allow **user config** to add custom aliases via pack enablement/keywords.

**Rationale**: Minimizes noise while still covering common shortcuts.

## 4) Performance Budget Per Pack

**Decision**: Enforce a per-pack budget and pattern cap.

- **Budget**: < 500 microseconds per pack evaluation.
- **Pattern cap**: < 50 total patterns per pack.
- **Keywords**: minimal and specific; avoid broad single-letter keywords.

**Rationale**: Keeps pack expansion from degrading hook latency.

## 5) Safe vs Destructive Threshold

**Decision**: Flag-aware matching with explicit safe overrides.

- **Destructive by default**: commands known to delete or destroy data (e.g.,
  `rclone sync`, `aws s3 rm --recursive`) should be blocked even without flags.
- **Flag-sensitive**: allow **dry-run/preview** flags and safe variants.
- **Ambiguous commands**: only block when flags or subcommands are clearly
  destructive; otherwise allow with high-signal patterns.

**Rationale**: Reduces false positives while still blocking dangerous actions.

## Acceptance Criteria Mapping

- Decisions documented with rationale (this doc).
- Performance budget defined and referenced by pack checklist.
- Pack overlap resolution aligned with `git_safety_guard-qdhh`.

## Open Questions

- Which alias list should be system defaults vs per-user config?
- How to represent host allowlists for REST APIs in pack metadata?

---

## File: docs/design/rule-metrics-struct-mapping.md

# Rule-Level Metrics: Struct Mapping & Reuse Strategy

> Design document for `git_safety_guard-1dri.1`
>
> This document inventories existing history analytics structs and maps rule-level
> metrics needs to them, ensuring no duplicate or competing analytics pipelines.

---

## Executive Summary

**Finding:** The existing history analytics structs in `src/history/schema.rs` are
well-designed and sufficient for rule-level metrics. No new analytics structs are
needed. The `PatternEffectiveness` struct already captures per-rule metrics, and
the `rule_id` field in `CommandEntry` provides stable rule identifiers.

**Recommendation:** Reuse existing structs. Extend query/aggregation logic only.

---

## Struct Inventory

### Core Data Types

| Struct | Location | Purpose | Rule-Metrics Relevance |
|--------|----------|---------|------------------------|
| `CommandEntry` | L133 | Single command record | **Primary data source** - contains `rule_id`, `pack_id`, `pattern_name`, `outcome` |
| `Outcome` | L81 | Enum: Allow/Deny/Warn/Bypass | Categorizes command outcomes for metrics |

### Statistics Types

| Struct | Location | Purpose | Rule-Metrics Relevance |
|--------|----------|---------|------------------------|
| `OutcomeStats` | L230 | Aggregate counts (allowed/denied/warned/bypassed) | Can aggregate by rule |
| `PerformanceStats` | L239 | Latency percentiles (p50/p95/p99/max) | Could extend for per-rule latency |
| `PatternStat` | L248 | Pattern name + count + pack_id | **Direct reuse** for top patterns |
| `HistoryStats` | L325 | Complete stats for time window | Contains `top_patterns: Vec` |
| `StatsTrends` | L271 | Period-over-period comparison | Extendable for rule trends |

### Analytics Types (Key for Rule-Metrics)

| Struct | Location | Purpose | Rule-Metrics Relevance |
|--------|----------|---------|------------------------|
| `PatternEffectiveness` | L2177 | Per-pattern metrics with bypass analysis | **PERFECT FIT** - use directly |
| `PotentialGap` | L2195 | Dangerous commands that were allowed | Coverage gap analysis |
| `RecommendationType` | L2208 | Enum: RelaxPattern/EnablePack/etc. | Tuning recommendations |
| `PackRecommendation` | L2225 | Actionable recommendation with config | Extend for rule-specific recs |
| `PackEffectivenessAnalysis` | L2246 | Complete analysis result | Already groups by pattern |

---

## Rule Identifier Design

The `rule_id` is already defined in `CommandEntry`:

```rust
/// Stable rule identifier: `pack_id:pattern_name`
/// Present only for denied commands that matched a pattern.
/// Format: "core.git:reset-hard", "core.filesystem:rm-rf-root"
pub rule_id: Option<String>,
```

**Key methods in `CommandEntry`:**
- `compute_rule_id()` → Constructs `pack_id:pattern_name`
- `get_rule_id()` → Returns stored or computed value
- `ensure_rule_id()` → Sets `rule_id` if computable

This provides stable, human-readable identifiers for:
- Allowlisting specific rules
- Aggregating metrics per rule
- Tracking rule effectiveness over time

---

## PatternEffectiveness: The Core Struct

This struct is the foundation for rule-level metrics:

```rust
pub struct PatternEffectiveness {
    /// Pattern name (e.g., "reset-hard")
    pub pattern: String,
    /// Pack ID the pattern belongs to (e.g., "core.git")
    pub pack_id: Option<String>,
    /// Total times this pattern triggered (deny + bypass)
    pub total_triggers: u64,
    /// Times the pattern blocked a command (deny)
    pub denied_count: u64,
    /// Times the pattern was bypassed (allow-once)
    pub bypassed_count: u64,
    /// Bypass rate as a percentage (0.0-100.0)
    pub bypass_rate: f64,
}
```

**Mapping to rule_id:** `rule_id = {pack_id}:{pattern}`

**Already computed in `PackEffectivenessAnalysis`:**
- `high_value_patterns: Vec` - High volume, low bypass
- `potentially_aggressive: Vec` - High bypass rate

---

## Reuse vs Extend Analysis

### Direct Reuse (No Changes)

| Struct | Usage |
|--------|-------|
| `CommandEntry` | Query by `rule_id` for per-rule history |
| `PatternEffectiveness` | Per-rule metrics with bypass analysis |
| `PatternStat` | Simple name+count for top rules |
| `OutcomeStats` | Aggregate outcomes per rule |

### Extend (Add Fields/Methods)

| Struct | Extension | Reason |
|--------|-----------|--------|
| `PatternEffectiveness` | Add `first_seen_ts`, `last_seen_ts` | Track rule activity timeline |
| `PatternEffectiveness` | Add `projects: Vec<String>` | See which projects trigger rule |
| `PackEffectivenessAnalysis` | Add `per_rule_metrics: HashMap<String, PatternEffectiveness>` | Direct rule_id lookup |

### Do NOT Create

To avoid competing pipelines, these should **NOT** be created:

- ~~`RuleMetrics`~~ → Use `PatternEffectiveness`
- ~~`RuleEffectiveness`~~ → Use `PatternEffectiveness`
- ~~`RuleStats`~~ → Use `PatternStat` with pack_id
- ~~`RuleAnalysis`~~ → Use `PackEffectivenessAnalysis`

---

## Query Patterns for Rule-Level Metrics

### Per-Rule Trigger Count

```sql
SELECT rule_id, COUNT(*) as triggers
FROM commands
WHERE rule_id IS NOT NULL
GROUP BY rule_id
ORDER BY triggers DESC;
```

### Per-Rule Bypass Rate

```sql
SELECT rule_id,
       COUNT(*) as total,
       SUM(CASE WHEN outcome = 'deny' THEN 1 ELSE 0 END) as denied,
       SUM(CASE WHEN outcome = 'bypass' THEN 1 ELSE 0 END) as bypassed,
       (bypassed * 100.0 / total) as bypass_rate
FROM commands
WHERE rule_id IS NOT NULL
GROUP BY rule_id;
```

### Rule Activity Over Time

```sql
SELECT rule_id,
       date(timestamp) as day,
       COUNT(*) as triggers
FROM commands
WHERE rule_id IS NOT NULL
GROUP BY rule_id, day
ORDER BY rule_id, day;
```

---

## Implementation Guidance

### For `dcg history stats --rule <rule_id>`

1. Query `commands` table filtered by `rule_id`
2. Compute `OutcomeStats` from results
3. Return `PatternEffectiveness` struct populated from query
4. No new structs needed

### For `dcg history analyze`

The existing `PackEffectivenessAnalysis` already computes `PatternEffectiveness`
for each pattern. To add rule-level access:

1. Add `rule_id` to `PatternEffectiveness` (computed from pack_id + pattern)
2. Add `by_rule_id: HashMap<String, PatternEffectiveness>` to analysis result
3. No new analysis pipeline needed

### For Rule-Specific Recommendations

Extend `PackRecommendation` with:

```rust
/// Rule ID this recommendation targets (if rule-specific)
#[serde(skip_serializing_if = "Option::is_none")]
pub rule_id: Option<String>,
```

---

## Acceptance Criteria Verification

| Criterion | Status | Evidence |
|-----------|--------|----------|
| Rule-metrics design references existing structs | ✅ | Uses `PatternEffectiveness`, `PatternStat`, `CommandEntry` |
| No duplicate analytics pipelines | ✅ | No new `Rule*` structs proposed |
| Clear mapping documented | ✅ | This document |

---

## Version History

| Version | Date | Changes |
|---------|------|---------|
| 1.0 | 2026-01-16 | Initial design document |

---

## Related Issues

- **Parent:** `git_safety_guard-1dri` (Rule-Level Metrics: Track per-rule stats)
- **Blocked by:** None
- **Blocks:** `git_safety_guard-1dri.2` (dcg history stats --rule)