# AGENTS.md

## Purpose

This repository contains a Python-based RimWorld modpack tool.

Agents working in this repository must treat the project as a spec-driven implementation. Code, tests, and documentation should follow the spec files in `specs/`.

Primary tooling assumptions:

- Python
- strict `basedpyright`
- `ruff`
- `pytest`

---

## Core working rules

1. Read the relevant spec files before changing code.
2. Treat the spec files in `specs/` as authoritative unless the task explicitly changes the spec.
3. Keep changes small, local, and easy to review.
4. Prefer explicit, simple designs over clever or implicit ones.
5. Do not introduce format or schema changes silently.
6. When behavior changes, update tests and docs in the same change.
7. Preserve determinism and diff-friendliness in generated outputs.

---

## Authoritative spec files

Agents should treat these files as the primary source of truth for data formats and behavior:

- `specs/modpack_folder_layout_spec.md`
- `specs/rimworld_modpack_list_format_spec.md`
- `specs/pack_toml_spec.md`
- `specs/ordering_toml_spec.md`
- `specs/activation_toml_spec.md`

If `specs/README.md` exists, read it first for reading order and relationships between specs.

---

## Repository expectations

This project should be implemented as a Python codebase with:

- strong static typing
- strict `basedpyright` compliance
- `ruff` compliance
- `pytest` test coverage

Agents should write code that is:

- typed
- modular
- readable
- deterministic
- easy to test

Avoid magical behavior, hidden global state, and unnecessary abstraction.

---

## Python standards

### Typing
- All new Python code should include explicit type annotations.
- Code must pass strict `basedpyright`.
- Prefer precise types over `Any`.
- Avoid untyped dictionaries for structured data when a `dataclass`, `TypedDict`, or protocol is more appropriate.
- Keep public function signatures stable and explicit.

### Style
- Follow `ruff` rules configured by the repository.
- Prefer clear, boring code over compact cleverness.
- Keep functions focused.
- Prefer `pathlib.Path` over raw path strings in Python code where practical.
- Prefer `dataclasses` for structured internal models unless a different choice is clearly justified.

### Testing
- All behavior changes should include or update tests.
- Use `pytest`.
- Favor small, targeted unit tests.
- Add regression tests for parser, resolver, activation, and ordering behavior when relevant.
- Keep tests deterministic.
- When tests exercise config loading or install-path discovery, isolate them from machine-local state by overriding the config path and clearing or setting relevant environment variables explicitly.
- Whenever changing Python source files, run both `ruff` and `basedpyright` before finishing the task.

---

## Implementation priorities

When implementing features, preserve these priorities:

1. Correctness against spec
2. Deterministic output
3. Preservation of user-authored text where required
4. Readability and maintainability
5. Performance only after correctness and clarity

Do not trade away spec correctness for speculative optimization.

---

## Format-specific guidance

### `.list` files
- Treat `.list` files as text-first authored documents.
- Preserve untouched lines exactly.
- Preserve comments and blank lines.
- Preserve unknown lines unchanged.
- Do not rewrite whole files just to normalize formatting.
- Apply the smallest practical edit for UI-driven changes.
- Parsers and editors for `.list` files should retain enough source structure to support comment- and formatting-preserving edits later, including raw line text and original line endings.

### `pack.toml`
- Treat `pack.toml` as a small root manifest.
- Do not store full mod lists in it.
- Do not store generated lock data, diagnostics payloads, caches, or UI state in it.
- Preserve comments and unrelated fields when editing if the TOML library supports it.
- Prefer preservation-friendly parsing/editing models over lossy deserialize-then-reserialize approaches, because future programmatic edits must keep comments, section layout, and formatting stable where possible.

### ordering and activation rules
- Keep ordering rules separate from activation rules.
- Ordering rules constrain relative order.
- Activation rules control whether candidate entries are active.
- Do not merge these concepts into one file or one resolver stage unless the spec is explicitly changed.
- The central load-order implementation lives in `rimpack.core.modpack.modpack.generate_modpack_load_order`; update that function whenever ordering behavior changes, including authored ordering rules or `About.xml`-derived ordering metadata.

---

## Change management rules

### If code changes require a spec change
Update the relevant file in `specs/` in the same change.

### If a spec is ambiguous
Do not invent a large interpretation silently.
Prefer the smallest reasonable implementation that matches the current wording.
If necessary, leave a clear note in code comments or task notes about the ambiguity.

### If a requested change conflicts with the existing spec
Update the spec first or as part of the same change. Do not leave code and spec drifting apart.

---

## File organization guidance

Agents should respect the intended separation between:

- authored project files
- generated files
- rules
- lock data
- tests
- implementation code

Do not mix generated artifacts into authored source directories.

Do not place agent scratch files, temporary exports, or local caches into the repository unless explicitly asked.

### Namespace boundaries
- `rimpack.core.mods` is reserved for parsing RimWorld mod files and folders such as `About.xml`, `LoadFolders.xml`, and on-disk mod directories.
- Modpack-authored project formats such as `.list`, `pack.toml`, and rule files should live outside `rimpack.core.mods`.
- Do not place modpack project parsers into the RimWorld mod metadata namespace.
- Whenever modpack parsing or editing code changes for `.list`, `pack.toml`, or rule files, also check whether `rimpack init` still creates the right authored structure and update it in the same change if needed.

---

## Testing expectations by area

### Parser changes
Add tests for:
- valid entries
- disabled entries
- comments
- blank lines
- unknown lines
- preservation-sensitive behavior

When changing code that parses RimWorld mod files or folders, also check for a local Steam RimWorld installation or workshop library and, if available, test the change against real mods in addition to unit tests. Prefer using the repository CLI command `rimpack load-mods` for this real-life parser verification.

### Manifest changes
Add tests for:
- required fields
- path handling
- ordering of `mod_files`
- ordering of rule file arrays
- invalid manifest shapes

### Ordering changes
Add tests for:
- stable ordering
- before/after constraints
- missing references
- cycle diagnostics

### Activation changes
Add tests for:
- `when_all_present`
- `when_all_absent`
- `when_any_present`
- `when_any_absent`
- inactive-by-rule behavior
- duplicate rules for the same mod if applicable

### Lock-file changes
Add tests for:
- deterministic output
- resolved entries
- unresolved entries
- disabled entries
- final load order contents

---

## Preferred implementation style

Prefer code that is easy for another agent or human to continue.

Good traits:

- small modules
- explicit models
- pure functions where practical
- thin I/O boundaries
- minimal hidden coupling
- resolve external context once at the boundary, then pass it into helpers explicitly instead of having deep helpers rediscover config, environment, or filesystem state

Avoid:

- giant all-in-one classes
- vague helper utilities with mixed responsibilities
- schema logic duplicated across multiple modules
- silent fallback behavior that hides invalid states
- helpers that reach back into config or environment discovery when the needed value can be passed as an argument

---

## When editing generated-output logic

Generated files should be:

- reproducible
- stable in ordering
- low-noise in diffs
- straightforward to inspect

Avoid timestamps or nondeterministic identifiers unless explicitly required by spec.

---

## Documentation expectations

When introducing a new persistent format, schema field, rule type, or pipeline stage:

1. update the relevant spec in `specs/`
2. update tests
3. update any code-level docs or README references if needed

Do not let implementation-only knowledge become the source of truth for a persisted format.

---

## Command assumptions

Use repository-configured commands for:

- linting with `ruff`
- type checking with `basedpyright`
- tests with `pytest`

If command names are defined in project files such as `pyproject.toml`, follow those definitions rather than inventing new ones.

---

## Summary for agents

When in doubt:

- read the relevant spec first
- keep code explicit
- preserve authored text carefully
- keep generated output deterministic
- update tests with behavior changes
- keep code, tests, and specs aligned
