# `activation.toml` / Activation Rule Files Spec

## Purpose

Activation rule files define explicit authored conditions that determine whether certain candidate mod entries should participate in the final resolved mod set.

This is especially useful for patch mods that should only be active when certain other mods are present or absent.

Examples:

- include patch P only if mod A and mod B are present
- include patch P only if mod C is absent
- include patch P only when a specific DLC is active

Activation rule files are applied after entry resolution and before ordering rules.

---

## Design principles

1. Activation rules control whether candidate entries are active.
2. Activation rules are separate from ordering rules.
3. Base modpack structure still comes from `.list` files.
4. Activation rules should use resolved mod identities, usually package IDs.
5. Patch mods remain authored in `.list` files even when activation conditions are not satisfied.
6. Inactive-by-rule is not the same as manually disabled.

---

## Relationship to the resolution pipeline

Recommended pipeline:

1. parse `pack.toml`
2. parse `mods/*.list`
3. resolve enabled entries to canonical identities
4. evaluate activation rules
5. mark entries active or inactive
6. apply ordering rules to active resolved entries
7. generate diagnostics or resolved load-order output

Activation rules must be evaluated after resolution, because conditions should usually be based on canonical identities rather than raw authored text.

---

## Relationship to `pack.toml`

Activation rule files are declared explicitly in `pack.toml`.

Example:

```toml
[paths]
activation_rule_files = [
  "rules/activation/40_patches.toml",
]
```

The application should:

1. read files in listed order
2. parse all activation rules
3. merge them into one activation rule set
4. preserve source-file information for diagnostics

Do not use nested include/import mechanisms inside activation rule files.

---

## File format

Activation rule files should use TOML.

Recommended paths:

```text
rules/activation/40_patches.toml
rules/activation/90_experimental.toml
```

Reasons to prefer TOML:

- comments are supported
- repeated rule blocks are readable
- files remain comfortable to edit by hand
- Git diffs are clearer than equivalent JSON

---

## Rule scope

Activation rules are intended for candidate mod entries, especially patch mods.

A candidate entry may be:

- manually disabled in a `.list` file
- enabled in a `.list` file but inactive by rule
- enabled and active

Only enabled, resolved, active entries should participate in the final resolved mod set.

---

## Rule schema

Each rule is represented by a `[[rules]]` table.

### Required fields

#### `mod`
Type: string

Reference to the candidate mod entry whose activation is being controlled.

This should use the same reference syntax as `.list` files.

#### At least one condition field
A rule should include at least one condition field.

Supported condition fields for v1:

- `when_all_present`
- `when_all_absent`
- `when_any_present`
- `when_any_absent`

### Optional fields

#### `reason`
Type: string

Human-readable explanation for why the rule exists.

#### `enabled`
Type: boolean

Optional explicit enable/disable flag for the rule.

This is optional if authors are comfortable commenting out rules.

---

## Supported reference syntax

Activation rule references use the same syntax as `.list` files.

Supported forms:

```text
steam:2009463077
packageid:brrainz.harmony
packageid:Ludeon.RimWorld
packageid:Ludeon.RimWorld.Royalty
packageid:Ludeon.RimWorld.Ideology
packageid:Ludeon.RimWorld.Biotech
packageid:Ludeon.RimWorld.Anomaly
local:MyFork
```

For activation logic, prefer canonical references such as `packageid:...`.

---

## Condition fields

### `when_all_present`
Type: array of strings

The rule passes only if all listed references are active and resolved.

Example:

```toml
when_all_present = [
  "packageid:mod.a",
  "packageid:mod.b",
]
```

### `when_all_absent`
Type: array of strings

The rule passes only if all listed references are absent from the active resolved set.

Example:

```toml
when_all_absent = [
  "packageid:mod.c",
]
```

### `when_any_present`
Type: array of strings

The rule passes only if at least one listed reference is active and resolved.

Example:

```toml
when_any_present = [
  "packageid:mod.a",
  "packageid:mod.b",
]
```

### `when_any_absent`
Type: array of strings

The rule passes only if at least one listed reference is absent from the active resolved set.

Example:

```toml
when_any_absent = [
  "packageid:mod.c",
  "packageid:mod.d",
]
```

---

## Examples

### Patch for A + B, but not C

```toml
[[rules]]
mod = "packageid:patch.ab_without_c"
when_all_present = [
  "packageid:mod.a",
  "packageid:mod.b",
]
when_all_absent = [
  "packageid:mod.c",
]
reason = "Patch only valid for A+B without C"
```

### Patch for A only when B is absent

```toml
[[rules]]
mod = "packageid:patch.a_only"
when_all_present = [
  "packageid:mod.a",
]
when_all_absent = [
  "packageid:mod.b",
]
reason = "Enable A-only patch when B is not installed"
```

### Patch that works with any of several mods

```toml
[[rules]]
mod = "packageid:patch.alt"
when_any_present = [
  "packageid:mod.a",
  "packageid:mod.b",
]
reason = "Alternative patch for either A or B"
```

### DLC-gated patch

```toml
[[rules]]
mod = "packageid:patch.biotech_support"
when_all_present = [
  "packageid:Ludeon.RimWorld.Biotech",
]
reason = "Only active when Biotech is enabled"
```

---

## Evaluation model

For v1, a candidate entry controlled by an activation rule is active only if its rule passes.

A rule passes when all provided condition fields pass.

That means:

- every `when_all_present` reference is present
- every `when_all_absent` reference is absent
- at least one `when_any_present` reference is present, if the field exists
- at least one `when_any_absent` reference is absent, if the field exists

If any provided condition field fails, the rule fails.

---

## Multiple rules for the same mod

Recommended v1 policy:

- allow at most one activation rule per controlled `mod`

This keeps the model simple and avoids unclear merge semantics.

If multiple rules are found for the same `mod`, the tool should emit a diagnostic.

---

## Inactive-by-rule behavior

If a candidate entry is:

- enabled in `.list`
- resolved successfully
- but fails its activation rule

then it should be marked inactive for this resolution run.

It should:

- remain present in authored files
- remain visible in diagnostics and lock data
- not appear in final load order

This is not the same as manual disable via `!` in a `.list` file.

---

## Interaction with final load order

Only entries that are:

- enabled in the source `.list`
- resolved successfully
- active after activation-rule evaluation

should participate in the final resolved mod set or final load order.

---

## Diagnostics

Activation rule failures are not always errors.

Recommended diagnostic categories:

- informational: rule not satisfied, entry inactive
- warning: activation rule references unknown target
- warning: duplicate activation rule for same mod
- error: invalid rule schema

Activation diagnostics should preserve source-file information when possible.

---

## What activation rules should not do

Activation rules should not:

- replace sensible `.list` file organization
- become a general expression language in v1
- use free-form boolean expressions in strings
- implement nested include systems
- silently rewrite source `.list` files
- serve as ordering constraints

Ordering remains the job of ordering rule files.

---

## Scaling guidance

If activation rules become large, prefer splitting them into multiple files.

Example:

```toml
[paths]
activation_rule_files = [
  "rules/activation/40_patches.toml",
  "rules/activation/90_experimental.toml",
]
```

This is preferable to one giant monolithic rule file.

---

## Suggested agent guidance

The agent should follow these rules:

1. Treat activation rules as separate from ordering rules.
2. Evaluate activation rules after resolution and before ordering.
3. Use the same mod reference syntax as `.list` files.
4. Prefer canonical references such as `packageid:...` in activation conditions.
5. Support at most one activation rule per controlled mod in v1.
6. Keep authored patch mods in `.list` files even when inactive by rule.
7. Represent inactive-by-rule state explicitly in generated output.
8. Do not invent a full boolean expression language for v1.

---

## Compact machine-oriented summary

```text
activation rule files:
- TOML files declared by [paths].activation_rule_files in pack.toml
- each file contains repeated [[rules]] tables

rule schema:
- mod = "<candidate mod reference>"
- optional condition arrays:
  - when_all_present
  - when_all_absent
  - when_any_present
  - when_any_absent
- optional reason
- optional enabled

behavior:
- evaluate after resolution
- conditions use same reference syntax as .list files
- prefer packageid references
- one activation rule per mod in v1
- enabled but inactive-by-rule entries stay in lock output
- only enabled + active + resolved entries reach the final resolved mod set
```
