# Agent Preferences and Guidelines

This file contains preferences and guidelines for AI agents working on this project. Please read this before making changes.

## Language and Spelling

- **Always use British spelling** (e.g., "colour", "organise", "centre", "realise")
- Use British English conventions throughout
- **Punctuation**: Prefer standard British punctuation (commas, parentheses, colons, semicolons). Use **em dashes sparingly**; do not habitually set off asides with `— … —`. Spaced en dashes (`–`) are fine for ranges (e.g. Chapters 1–9). **Occasional long sentences are welcome** when the logic stays clear; do not force a short-sentence style. See `.cursor/rules/british-prose-punctuation.mdc`.
- **Chapter openings and voice**: Treat readers as sophisticated adults. Do **not** use schoolbook framing (`## Learning Objectives`, “After reading this chapter, you will be able to…”, “you will learn to…”, and similar checklists). Let the **Introduction** (or Overview) state what the chapter argues or develops; aims should be implied in that prose. Avoid preschool reassurance (“don’t worry”, “easy!”). Prefer Julia-manual tone in front matter and package-facing prose: define first, then show code; avoid marketing (“showcase”) and tutorial cheerleading. See `.cursor/rules/chapter-openings.mdc`.

## General principles 

For every complex problem:
1.DECOMPOSE: Break into sub-problems
2.SOLVE: Address each with explicit confidence (0.0-1.0)
3.VERIFY: Check logic, facts, completeness, bias
4.SYNTHESIZE: Combine using weighted confidence
5.REFLECT: If confidence <0.8, identify weakness and retry
For simple questions, skip to direct answer.

Always output:
∙Clear answer
∙Confidence level
∙Key caveats

## Code Style

### General Principles

- **Prioritise clarity and maintainability** by choosing simple, direct solutions
- Break complex tasks into smaller, focused functions
- Name functions based on their actions
- Use descriptive identifiers for variables, arguments, and objects to clearly indicate their roles
- **Always include docstrings** following the standard practice for the target programming language
- **CRITICAL: Check existing implementations before creating new code**: Before implementing any new functionality, **always check**:
  - `~/Documents/Work/Packages/` (especially `~/Documents/Work/Packages/forks/` and `~/Documents/Work/Packages/owned/`) for existing implementations
  - `./packages/` for local package forks and submodules
  - Existing scripts in `./scripts/` that might already provide the functionality
  - Never implement something that already exists elsewhere - use or extend existing code instead

### Julia-Specific

- Use the `ensure_packages.jl` utility for package management:
  ```julia
  include("scripts/ensure_packages.jl")
  @auto_using Package1 Package2
  ```
- For plotting with Makie, use SVG output for responsive HTML:
  ```julia
  using CairoMakie
  CairoMakie.activate!(type = "svg")
  ```
- Use `fig-width: 100%` in Quarto chunk options for responsive figures
- Prefer relative sizing or let Quarto handle scaling rather than fixed pixel sizes
- Generally, **use Greek letters and full Unicode in Julia code** when they appear in the surrounding mathematical notation, so that code matches the equations (see **Mathematical Notation** section below for details).

#### Handling Name Conflicts and Ambiguities

When multiple packages export the same name (e.g., `Categorical` from both `Distributions` and `CairoMakie`), Julia will raise an `UndefVarError` with an ambiguity hint. **Always resolve these by using fully qualified names**:

- **Use package-qualified names**: When a name is ambiguous, use the full package path:
  ```julia
  # Bad (ambiguous):
  cat = Categorical([0.3, 0.7])
  
  # Good (explicit):
  cat = Distributions.Categorical([0.3, 0.7])
  ```

- **Common conflicts to watch for**:
  - `Categorical`: Exported by both `Distributions` and `CairoMakie` → Use `Distributions.Categorical` for probability distributions
  - `value`, `uncertainty`: Exported by `Measurements` and other packages → Use `Measurements.value` and `Measurements.uncertainty`
  - `norm`: Exported by `LinearAlgebra` and other packages → Use `LinearAlgebra.norm` or import explicitly

- **When in doubt**: If you see an ambiguity error, check which package you actually need and use the fully qualified name. This makes the code clearer and avoids runtime errors.

## Markdown Formatting

See `.cursorrules` for detailed markdown formatting rules. Key points:

- **Always add empty lines** before and after lists, headings, code blocks, blockquotes, and horizontal rules
- **Headings start at level 2 (##)**: The YAML frontmatter `title:` provides the level 1 heading, so markdown headings should start at `##` (not `#`)
- Maintain proper heading hierarchy (don't skip levels)
- Use consistent spacing (single empty line between paragraphs, maximum 2 consecutive empty lines)
- Follow Quarto-specific formatting rules

## File Structure

- Code examples should be self-contained and work in both Quarto and standalone Julia
- Use relative paths for includes (e.g., `include("scripts/ensure_packages.jl")`)
- Keep utility functions in `scripts/` directory

## Julia Execution

- **Always use project and threads**: When running Julia commands, always use `julia --project=. --threads=auto` to ensure the correct project environment and optimal thread usage.

## Package Version Management

### Local Package Forks (Git Submodules)

**CRITICAL**: Forked Julia packages are **git submodules** under `packages/` and must be initialised after clone. **Agents.jl** is **not** a submodule—it is installed from the **General** registry when you `Pkg.instantiate()` (see below).

#### Local Package Structure

Git submodules under `packages/` (forks / book-specific upstreams):

- `packages/GraphMakie.jl` - GraphMakie.jl fork (v0.6.3) from https://github.com/SimonAB/GraphMakie.jl.git
- `packages/UniversalDiffEq.jl` - UniversalDiffEq.jl fork from https://github.com/SimonAB/UniversalDiffEq.jl.git
- `packages/CausalInference.jl` - CausalInference.jl fork from https://github.com/SimonAB/CausalInference.jl.git (dagitty-like ID; develop here before any upstream PR — see `packages/CausalInference.jl/FORK.md`)

**Owned packages** (SimonAB; synced to `~/Documents/Work/Packages/owned/` via `scripts/sync_packages.jl`):

- `packages/CausalDynamics.jl` - CausalDynamics.jl from https://github.com/SimonAB/CausalDynamics.jl.git
- `packages/CausalTargeted.jl` - CausalTargeted.jl from https://github.com/SimonAB/CausalTargeted.jl.git (targeted LMTP/mediation estimation; depends on CausalDynamics)
- `packages/DAGMakie.jl` - DAGMakie.jl from https://github.com/SimonAB/DAGMakie.jl.git (visualisation only; identification via CausalInference)
- `packages/GlobalSensitivity.jl` - Vendored **GlobalSensitivity** v2.11.0 (SciML): Sobol `generate_design_matrices` uses `SobolSample(R = Shift())` so precompilation does not emit the QuasiMonteCarlo `NoRand` warning. Replace from upstream when updating the dependency.
- `packages/DiagrammaticEquations.jl` - Vendored **DiagrammaticEquations** v0.2.5 (AlgebraicJulia): explicit imports for Julia 1.12 constructor extension warnings. Replace from upstream when updating the dependency.

**Package design principles** (read before adding features): shared [packages/DESIGN_PRINCIPLES.md](packages/DESIGN_PRINCIPLES.md); per-package [CausalDynamics.jl/DESIGN.md](packages/CausalDynamics.jl/DESIGN.md), [CausalTargeted.jl/DESIGN.md](packages/CausalTargeted.jl/DESIGN.md), [DAGMakie.jl/DESIGN.md](packages/DAGMakie.jl/DESIGN.md). Scope boundaries: each package’s `BOUNDARIES.md`.

Both are wired via `[sources]` in `Project.toml` (paths under `packages/`); they are **not** git submodules—commit changes in-tree or refresh from release tags when bumping versions.

**Agents.jl** is **not** a submodule: it is resolved from the **Julia General** registry. The book targets the **v7** API (`@agent`, `StandardABM`, …). Use `[compat] Agents = "7"` in `Project.toml` and the locked version in `Manifest.toml` for reproducibility (`Pkg.instantiate()` after clone).

#### Critical Dependency Versions

**DO NOT CHANGE THESE VERSIONS WITHOUT EXPLICIT APPROVAL**:

- **Agents.jl**: Must be **v7.x** from the **General** registry (JuliaDynamics); book examples target the v7 `@agent` / `StandardABM` API
- **GraphMakie.jl**: Must be **v0.6.3** from `packages/GraphMakie.jl` (local fork with custom changes)
- **Makie.jl**: Must be **v0.24.8** (required by GraphMakie 0.6.3)
- **DAGMakie.jl**: Must be from `packages/DAGMakie.jl` (owned package)

**Why these versions matter**:

- Agents 7 follows the current JuliaDynamics API (`StandardABM`, read-only `id`, `GraphSpace(graph)` only, extensions for OSM/visualisation)
- GraphMakie 0.6.3 has custom changes (auto-label-alignment feature) that are required
- GraphMakie 0.6.3 requires Makie 0.24.x (uses `documented_attributes` which doesn't exist in Makie 0.22)
- These versions work together as a tested, stable combination

**Agents.jl API Notes (v7)**:

- Define agents with `@agent` (e.g. `@agent struct Person(GraphAgent) … end`); do not assign `id` yourself—use `add_agent!(position, model, …)` or `add_agent!(model; …)` as documented.
- Use `StandardABM` for the usual discrete-time model (the `ABM` name refers to the abstract `AgentBasedModel` type).
- Prefer `abmrng(model)` and `abmspace(model)` inside stepping functions instead of reaching into internal fields.
- OpenStreetMap and several plotting paths require loading optional dependencies (`using LightOSM`, Makie backends) so extensions load; see upstream CHANGELOG.

#### Setting Up Local Packages

After cloning the repository, initialize all submodules:

```bash
git submodule update --init --recursive
```

Then develop the local packages in Julia:

```julia
using Pkg
Pkg.develop(path="packages/GraphMakie.jl")
Pkg.develop(path="packages/DAGMakie.jl")
Pkg.develop(path="packages/CausalDynamics.jl")
Pkg.develop(path="packages/CausalTargeted.jl")
Pkg.develop(path="packages/CausalInference.jl")
Pkg.develop(path="packages/UniversalDiffEq.jl")
Pkg.resolve()
```

#### Automated Package Syncing

**Automatic syncing is enabled via git hooks** to keep all package copies in sync:

- **When you commit in a submodule** (`./packages/*`): Changes are automatically synced to `~/Documents/Work/Packages/` (mapped packages in `scripts/sync_packages.jl`: CausalDynamics, CausalTargeted, DAGMakie, GraphMakie, UniversalDiffEq)
- **When you commit in Packages** (`~/Documents/Work/Packages/*`): Changes are automatically synced to the corresponding submodule

The sync system uses `scripts/sync_packages.jl` which:

- Detects which location has the latest committed changes
- Pulls the latest version to the other location
- Only syncs when commits are pushed to remotes (avoids syncing uncommitted work)

**Manual syncing**:
```bash
# Sync all packages
julia --project=. scripts/sync_packages.jl

# Sync a specific package
julia --project=. scripts/sync_packages.jl --package GraphMakie.jl

# Dry run (see what would be synced)
julia --project=. scripts/sync_packages.jl --dry-run
```

**Reinstalling hooks** (if needed):
```bash
./scripts/install_package_sync_hooks.sh
```

#### Updating Local Packages

To update a local package fork:

1. Navigate to the package directory: `cd packages/<Name>.jl` (other copies may still live under `~/Documents/Work/Packages/forks/` or `owned/` if you use hooks)
2. Make your changes and commit them
3. Push to remote: `git push origin main` (or appropriate branch)
4. The sync hook will automatically propagate changes to the other location
5. Return to project root and resolve dependencies: `julia --project=. --threads=auto -e 'using Pkg; Pkg.resolve()'`

**Warning**: Updating Agents.jl or GraphMakie.jl may break compatibility. Test thoroughly before committing.

#### Path Structure

- **Always use `packages/` path** for submodule forks: e.g. `packages/GraphMakie.jl`, not ad-hoc paths outside the repository
- **Project.toml**: The `[deps]` section lists packages by UUID, and `Pkg.develop()` sets the path in `Manifest.toml`
- **Never manually edit paths in Manifest.toml**: Use `Pkg.develop(path="packages/...")` instead

#### Troubleshooting

If you see errors about Agents being downgraded or GraphMakie compatibility issues:

1. **Check Manifest.toml**: Verify **Agents** is at **v7.x** from the registry and forked packages (`GraphMakie.jl`, …) use the expected `path = "packages/…"` entries
2. **Re-develop forked packages**: Run `Pkg.develop(path="packages/GraphMakie.jl")` (and other `packages/…` paths as needed), then `Pkg.resolve()`
3. **Check Makie version**: Ensure Makie is v0.24.8 (GraphMakie 0.6.3 requires it)
4. **Resolve dependencies**: Run `Pkg.resolve()` to ensure consistency

### Other Package Management

- **CausalDynamics.jl**: Uses latest compatible versions of all dependencies. `Pkg.update()` works successfully.
- **CDCS Project**: Uses UniversalDiffEq fork (https://github.com/SimonAB/UniversalDiffEq.jl) with updated compat constraints to allow latest package versions. Compat sections use major version constraints to allow automatic updates.
- **UniversalDiffEq Fork**: Forked to resolve dependency conflicts. The fork's `Project.toml` has been updated to:
  - Remove inline comments (TOML doesn't support them)
  - Allow latest versions: `AdvancedHMC = "^0.8"`, `Optimization = "^5.0"`, `DiffEqFlux = "^4.3"`, `OrdinaryDiffEq = "^6"`, `StochasticDiffEq = "^6"`, `julia = "1.12"`
- **UniversalDiffEq Dependency**: The project uses UniversalDiffEq via UUID in `Project.toml`. The fork (https://github.com/SimonAB/UniversalDiffEq.jl) should be added via `Pkg.add(url="https://github.com/SimonAB/UniversalDiffEq.jl.git")` if not already in the environment. The fork has updated compat constraints to allow latest package versions.
- **Setup Script**: Use `setup.jl` to configure the project after cloning. It sets up `CausalDynamics.jl` as a local dev dependency and ensures all packages are instantiated.
- **Policy**: For packages NOT listed above as local forks, always use the latest compatible versions. Compat sections use major version constraints (e.g., `"1"` for any 1.x version) to allow automatic updates.

## Quarto-Specific

- **Never use Jupyter**: Always use `engine: julia` for Quarto notebooks (`.qmd` files). Never use Jupyter/IJulia kernels. The project uses Quarto's native Julia engine exclusively.
- Code chunks should specify `engine: julia` in YAML frontmatter (or rely on project-level setting in `_quarto.yml`)
- For responsive figures in HTML: use SVG output with `CairoMakie.activate!(type = "svg")` and `fig-width: 100%`
- Ensure all code works in both Quarto preview and standalone Julia
- **Julia chunk warnings (native engine)**: **QuartoNotebookRunner** applies `Logging.disable_logging` only when a cell sets **`#| warning: false`**; project-level `execute.warning` in `_quarto.yml` is **not** merged into each cell’s options. Every `{julia}` chunk in this book should set **`#| warning: false`** unless you deliberately use **`#| warning: true`**. After adding new `{julia}` cells, run `julia --project=. --threads=auto scripts/qmd_julia_chunks_warning_false.jl` to patch chunks that omit a `warning` option. For rare cases that need a narrower scope than the whole cell, use **`quarto_suppress_logging_during`** from `scripts/ensure_packages.jl`.

### References and bibliography

- **Use `references.bib`**: The book bibliography is set in `_quarto.yml` (`bibliography: references.bib`). Add new entries there for anything that should appear in the reference list.
- **Citation syntax**: Use Quarto / Pandoc form—typically `[@citationKey]` for parenthetical citations, and `[@a; @b]` for several sources together. Put `doi` / `url` on the **bib entry**, not a bare markdown link in place of a cite for scholarly sources.
- **Figure captions and callouts**: Use the same `[@citationKey]` syntax inside `#| fig-cap: "..."` and in callout bodies when a source is needed.
- **Stable keys**: Prefer predictable keys (e.g. `authorYearShorttopic`). Use `@article`, `@book`, `@inproceedings`, or `@misc` (with `url` / `howpublished` when appropriate) so CSL output stays consistent.
- **Incidental links**: Repository paths, package docs, or one-off tooling may remain ordinary markdown links when they are not formal bibliography items.

### Code Chunk Requirements

- **Every code chunk must have a label**: Use `#| label: chunk-name` format for code chunks that should be referenced
- **Code chunks should be referenced when useful**: Reference code chunks in text when they're important (note: Quarto code chunk references may not work in all formats; use descriptive text references as fallback)
- **Figure chunks must have captions**: Use `#| fig-cap: "Caption text"` for all figure-generating chunks
- **Figures must be referenced**: Reference figures using `@fig-label` syntax in text
- **Hide figure code when appropriate**: Use `#| echo: false` for figure chunks to hide code while showing output
- **Label naming convention**: Use descriptive, hyphenated names (e.g., `chunk-state-space-model`, `fig-protein-concentration`)
- **Separate visualization code into its own chunk**: Always split visualization/plotting code (e.g., `let` blocks with `Figure`, `Axis`, plotting commands) into a separate chunk from the computation code. This improves readability, allows independent execution, and makes it easier to hide/show code. Use `#| echo: false` for visualization chunks. Name visualization chunks with a `-viz` suffix (e.g., `chunk-name-viz`). The visualization chunk should reference variables from the previous computation chunk.

## Mathematical Notation

### Code Should Match Mathematical Formulae

**Principle**: Julia code should use standard mathematical notation where possible, making code look as close as possible to the equations and mathematical formulae.

### Unicode and Mathematical Symbols

- Use Unicode mathematical symbols in variable names and code:
  - Greek letters: `α`, `β`, `γ`, `δ`, `ε`, `θ`, `λ`, `μ`, `σ`, `φ`, `ψ`, `ω`, etc.
  - Subscripts: `x₁`, `x₂`, `σ_w`, `σ_v` (use underscore for subscripts: `x_w` becomes `x_w`)
  - Superscripts: Use `²` for squared (e.g., `σ²`), `³` for cubed
  - Mathematical operators: `∑`, `∏`, `∫`, `√`, `∂`, `∇`, etc.
  - Set notation: `∈`, `∉`, `⊂`, `∪`, `∩`, etc.
  - Relations: `≤`, `≥`, `≠`, `≈`, `≡`, etc.

### Examples

**Good** (matches mathematical notation):
```julia
# Mathematical: x_{t+1} = A x_t + B u_t + w_t, where w_t ~ N(0, σ²_w)
x_{t+1} = A * x_t + B * u_t + w_t
w_t ~ Normal(0, σ_w²)

# Mathematical: Y_t = h(X_t) + v_t, where v_t ~ N(0, σ²_v)
Y_t = h(X_t) + v_t
v_t ~ Normal(0, σ_v²)

# Mathematical: θ = (α, β, γ)
θ = (α, β, γ)
```

**Avoid** (doesn't match mathematical notation):
```julia
# Don't use: x_next = A * x_current + B * u_current + process_noise
# Don't use: y_obs = h(x_state) + measurement_noise
```

### Variable Naming Conventions

- Use mathematical variable names from equations directly in code
- For subscripts, use underscore: `x_t`, `σ_w`, `u_{t-1}` (Julia supports `x_t` syntax)
- For Greek letters, use Unicode directly: `α`, `β`, `σ`, `θ`
- For vectors/matrices, use bold notation in comments but standard names in code: `X` for vector `\mathbf{X}`
- Time indices: use `t`, `t+1`, `t-1` as subscripts: `x_t`, `x_{t+1}`, `x_{t-1}`

### When to Use Mathematical Notation

- **Always** when the variable appears in mathematical equations in the text
- **Always** for standard mathematical symbols (Greek letters, operators)
- **Prefer** mathematical notation over descriptive names when the math is the primary reference
- Use descriptive names only when mathematical notation would be unclear or non-standard

### Julia Unicode Input

- In Julia, type `\alpha` + Tab for `α`, `\sigma` + Tab for `σ`, etc.
- Common shortcuts:
  - `\alpha` → `α`, `\beta` → `β`, `\gamma` → `γ`
  - `\sigma` → `σ`, `\theta` → `θ`, `\lambda` → `λ`
  - `\mu` → `μ`, `\phi` → `φ`, `\psi` → `ψ`
  - `\leq` → `≤`, `\geq` → `≥`, `\neq` → `≠`
  - `\in` → `∈`, `\sum` → `∑`, `\prod` → `∏`
  - `\sqrt` → `√`, `\partial` → `∂`, `\nabla` → `∇`

## Project Context

This is a technical book on **causal dynamics for complex systems**, written in Quarto with Julia code examples. It weaves Pearl-style structural causal semantics with dynamical systems, state-space inference, and graph-based mechanisms. Read **`index.qmd`** (preface, three strata, ladder of Reason) and **`introduction.qmd`** (tone, examples, CDM notation) before editing prose or code—these set the voice: rigorous, practical, and process-oriented.

The book is written from within **Whitehead’s process metaphysics** (*Process and Reality* and related work). That orientation shapes how the three strata and method placement are organised; we still use `$X_t$`, DAGs, `do(·)`, and package APIs where they are the clearest tools, and process terms where they clarify what the model is doing.

### Process philosophy (working orientation for agents)

**Core stance:** A complex system (organism, ecosystem, cohort) is a **process of becoming**. Modelling separates (i) **structural** invariances and relations among variables, (ii) **dynamical** unfolding of latent state, and (iii) **observable** traces of what occurred, obscured by noise. Pearl’s three levels of Reason (association, intervention, counterfactual) are the working query ladder.

**Use process jargon only where it helps.** Prefer ordinary causal and dynamical language (`observation`, `intervention`, `time step`, `terminal state`, `lagged influence`) unless a process term disambiguates. Define load-bearing terms once (Introduction, Concept Reference, Chapter 1); do not refrain them through mid-book chapters.

**Load-bearing terms** (keep; use sparingly after definition):

| Process term | Modelling role in this book | Prefer ordinary when… |
|--------------|----------------------------|------------------------|
| **Prehension** | Taking account of (edges, parents, $Y_t = h(X_t)$); Pearl’s rungs as modes of grasping | “Depends on”, “observes”, “influences” already clear |
| **Creative advance** | Pearl’s $\mathbf{u}$: unmodelled generative influence (not mere error) | Talking only about $\sigma_w$ / $\sigma_v$ measurement or process noise |
| **Alternative concrescences** | Same unit, same $\mathbf{u}$, different $do(\cdot)$ (L3) | Population L2 estimands $E[Y \mid do(a)]$ |

**Local / light use** (once at definition; thereafter ordinary words):

| Process term | Prefer instead |
|--------------|----------------|
| **Concrescence** | Transition, state update, integration, solve |
| **Actual occasion** | Time index $t$, step, tick |
| **Presentational immediacy** | Observation, measurement, assay ($Y_t$) |
| **Physical prehension** | Intervention, $do(\cdot)$, graph surgery |

**Glossary-only / avoid in running prose** (Table 4 may list them; do not sprinkle): **superject**, **eternal objects**, **causal efficacy**, **conceptual prehension**, **negative prehension**. Use **terminal state**, **invariance**, **lagged influence**, **sparsity / inhibition** instead.

**Three strata:**

- **Structural** — graphs, mechanisms, identification, invariances.
- **Dynamical** — how latent state unfolds through time (ODEs, SDEs, attractors, feedback).
- **Observable** — how the system is measured (filters, estimators, `$Y_t = h(X_t,·)$`).

**Voice:** British spelling; at most one process term per paragraph in technical chapters. Do not rename third-party APIs (`solve`, `graphplot!`, CausalDynamics.jl exports) for philosophical consistency.

### Julia code and process terminology

When writing or editing **book** Julia (`.qmd` chunks, `scripts/` utilities used by the book):

1. **Prefer clarity over ideology** — keep standard names (`simulate_cdm`, `σ_w`, `ODEProblem`, `terminal_state`) when they match the text or packages.
2. **Use process-aligned names only when they disambiguate** — e.g. comments noting shared `U` as creative advance for L3; do not invent `superject` identifiers.
3. **Docstrings and comments** — ordinary SciML/causal language first; optional gloss for the three load-bearing terms only.
4. **Do not force renames** across `packages/` submodules, upstream forks, or generic SciML/Makie APIs.
5. **After adding `{julia}` cells** — run `scripts/qmd_julia_chunks_warning_false.jl` (see Quarto-Specific above).

**Pedagogical helpers** (e.g. in Chapter 9): `coupled_unit_dynamics!`, `edge_widths_from_intensity`. Prefer dynamical names over Whitehead coinages in new code.

**Owned packages (`packages/CausalDynamics.jl`, `packages/CausalTargeted.jl`, `packages/DAGMakie.jl`)** — keep **standard causal/SciML API names** (`Intervention`, `do_surgery`, `find_backdoor_paths`, `DoIntervention`, …). Process vocabulary belongs in **book** prose (`concept-reference-tables.qmd` Table 8, Chapter 9), not in package manuals or module docstrings. Do **not** rename established terms when the Pearl name is clearer; do **not** add philosophy lectures in package docs.

**Book ↔ packages:** Chapters that use these packages may include `snippets/package-terminology.qmd` and point to `concept-reference-tables.qmd` Table 8. Book prose and pedagogical `.qmd` code may use process-aligned names when they disambiguate; package APIs and Documenter pages must not.

**Do not** add process renames to forked or vendored packages.

## Common Patterns

### Package Management Pattern
```julia
include("scripts/ensure_packages.jl")
@auto_using Package1 Package2
```

### Responsive Plotting Pattern
```julia
#| fig-width: 100%
include("scripts/ensure_packages.jl")
@auto_using CairoMakie
CairoMakie.activate!(type = "svg")
# ... plotting code ...
```

### State-Space Model Pattern
- Use `σ_w` and `σ_v` for standard deviations (matching mathematical notation)
- Use `σ_w²` and `σ_v²` for variances (matching mathematical notation)
- Explicitly name noise variables: `w` for process noise, `v` for measurement noise
- Use time subscripts: `x_t`, `y_t`, `u_t`, `w_t`, `v_t`
- Example: `x_{t+1} = A * x_t + B * u_t + w_t` (code matches equation)
- **Process gloss (comments/docstrings, optional):** only when helpful — `U` / $\mathbf{u}$ as creative advance (L3); shared-`U` counterfactuals as alternative concrescences; edges as prehensions. Prefer `Y_t` observation, `do(·)` intervention, `t` time step elsewhere.

### Diagram Conventions (Mermaid/State-Space Models)
- **Dashed outlines for latent variables**: In state-space model diagrams, use dashed outlines (`stroke-dasharray: 5 5`) for nodes representing latent/unobserved states (e.g., $X_t$)
- **Solid outlines for other variables**: Observed variables (e.g., $Y_t$) and exogenous noise variables (e.g., $U^x_t$, $U^y_t$) should use solid outlines
- **Rationale**: Dashed outlines specifically indicate latent states that are inferred from observations, distinguishing them from exogenous noise variables (which are also unobserved but represent random inputs rather than inferred states)
- **Visual distinction**: Exogenous noise variables are already distinguished by colour and grouping (separate subgraph), so they don't need dashed outlines

## Package Management and CI

### CausalDynamics.jl Loading

- **CausalDynamics.jl is not in Project.toml**: The package is loaded dynamically via `ensure_packages.jl` to avoid Project.toml format issues
- **How it works**: `ensure_packages.jl` checks if CausalDynamics is in project dependencies, and if not, runs `Pkg.develop(path="CausalDynamics.jl")` to add it
- **Dependencies**: When CausalDynamics is developed, its dependencies (Graphs, ModelingToolkit, Symbolics, ForwardDiff) are automatically installed
- **CI compatibility**: This approach works reliably in GitHub Actions because:
  - `Pkg.instantiate()` succeeds (no CausalDynamics in Project.toml)
  - `ensure_packages.jl` loads CausalDynamics when code chunks execute
  - All dependencies are standard Julia packages available on all platforms

### GitHub Actions Workflow

The GitHub Pages workflow (`.github/workflows/pages.yml`) checks out the repository (including **git submodules**), verifies pre-built `_book/` HTML, optionally copies `downloads/book.pdf` into `_book/downloads/`, and uploads the site artifact. It does **not** run Julia or Quarto on the runner—render locally or in another workflow before push if you update sources.

For Julia execution locally or on other CI, use `julia --project=. --threads=auto -e 'using Pkg; Pkg.instantiate()'` (which installs **Agents.jl** from the registry and materialises `[sources]` paths) before `quarto render`.

**Key point**: Code using CausalDynamics.jl will work in CI because:

- All functions are platform-independent (pure Julia/Graphs.jl)
- Dependencies are automatically installed when CausalDynamics is loaded
- No system-specific code or external dependencies

### Testing Before Pushing

Before committing changes that affect package loading or CI:
```bash
# Simulate CI workflow
julia --project=. --threads=auto -e 'using Pkg; Pkg.instantiate()'
quarto render --to html
```

## Integration Development

### Adding New Package Integrations

When adding integration with third-party packages:

1. **Code-level integration** (like TMLE.jl): Add integration functions in `src/integration/`
   - **Always add unit tests**: Any new integration functions must have corresponding unit tests in `test/test_integration.jl`
   - **Test error handling**: Test behaviour when optional packages are not loaded
   - **Test edge cases**: Empty graphs, disconnected nodes, invalid inputs, etc.
   - **Example**: TMLE.jl integration has 23 unit tests covering all integration functions, error handling, and edge cases

2. **Workflow-level integration** (like UniversalDiffEq.jl): Document in `docs/src/integration.md` and book chapters
   - **No tests needed**: Workflow-level integrations don't require tests (no code integration)
   - **Documentation sufficient**: Examples in book and docs show how to use both packages together
   - **Underlying functions already tested**: All CausalDynamics.jl functions used in workflows are already tested

3. **Document integration**: Update `docs/src/integration.md` and README.md for both types

**Decision rule**: If you add integration functions (code that calls other packages), add tests. If you only document how to use packages together (workflow), no tests needed.

## When Making Changes

1. Check `.cursorrules` for markdown formatting requirements
2. Ensure code works in both Quarto and standalone Julia
3. Use British spelling throughout
4. Include docstrings in functions
5. Test that plots are responsive in HTML output
6. **Verify package management works correctly**: Ensure **Agents.jl** is v7 per `Manifest.toml`, forked packages (GraphMakie.jl, …) and owned packages (CausalDynamics.jl, CausalTargeted.jl, DAGMakie.jl) use the expected `packages/` checkouts
7. **Do not downgrade Agents.jl below v7 or change GraphMakie.jl version** without explicit approval—the book source assumes Agents v7 and pinned Makie/GraphMakie
8. **Add labels to all code chunks** and reference them in text
9. **Add captions to all figures** using `fig-cap` option
10. **Reference code chunks in text** using `@chunk-label` syntax
11. **Test CI compatibility**: Run `quarto render` locally before pushing changes that affect code execution
12. **Add unit tests for integrations**: Any new package integration functions must have corresponding unit tests
13. **If package errors occur**: Check that all submodules are initialised (`git submodule status`) and local packages are developed correctly
14. **CRITICAL: Check existing implementations before creating new code**: Before implementing any new functionality, **always check**:
    - `~/Documents/Work/Packages/` (especially `~/Documents/Work/Packages/forks/` and `~/Documents/Work/Packages/owned/`) for existing implementations
    - `./packages/` for local package forks and submodules
    - Existing scripts in `./scripts/` that might already provide the functionality
    - Never implement something that already exists elsewhere - use or extend existing code instead
15. **Citations**: Add new scholarly references to `references.bib` and cite with `[@key]` in prose, callouts, and figure captions—avoid ad hoc URL-only “citations” for work that belongs in the bibliography.
16. **New `{julia}` cells**: Run `julia --project=. --threads=auto scripts/qmd_julia_chunks_warning_false.jl` so each chunk gets **`#| warning: false`** unless you explicitly set **`#| warning: true`** (see **Julia chunk warnings** under Quarto-Specific above).
17. **Process philosophy and code**: Use process jargon only where it helps (load-bearing: prehension, creative advance, alternative concrescences). Prefer ordinary causal/dynamical language elsewhere; keep glossary terms out of mid-book refrain. Package APIs stay Pearl/SciML; see **Project Context** above.
