# Packs

The pack format primitives. For the conceptual model and the authoring workflow see the [Authoring packs](https://docs.activegraph.ai/guides/authoring-packs/index.md) guide.

The shipped reference pack is [Diligence](https://docs.activegraph.ai/reference/api/packs/diligence/index.md).

## Pack declaration

A frozen bundle of pack contents.

Equality and hashing are by (name, version) — NOT by deep field comparison. Behaviors and tools are dataclasses (not hashable); full structural equality would not work and isn't what users care about. The identity that matters is "is this the same pack name and version" — that's what idempotent loading hinges on.

## `prompt_manifest()`

The `pack.loaded` payload's `prompts` block. Maps prompt name to {"version", "hash"}. CONTRACT v0.9 #10.

A typed object the pack contributes.

`schema` is a Pydantic `BaseModel` subclass. When the pack is loaded, `graph.add_object(name, data=...)` validates against it. Validation applies only to objects created AFTER the pack loads (CONTRACT v0.9 #5).

A typed relation the pack contributes.

`source_types` and `target_types` are tuples of object type names; empty means "any".

A policy declared by a pack.

`requires_approval`: tuple of object type names whose `add_object` is gated until `runtime.approve(...)` is called.

A versioned, content-hashed prompt.

`version` is the declared human-readable version (for changelogs and operator messages). `content_hash` is the SHA-256 of the body truncated to 16 hex chars; this is the **replay contract** (the hash, not the version — see CONTRACT v0.9 #10).

Bases: `BaseModel`

For packs with no configurable settings. The default `settings_schema`.

A Pydantic model with zero fields, so no-settings packs flow through the same typed-injection machinery as configurable ones (CONTRACT v0.9 #7) — behaviors can still ask for `settings` and get a real instance rather than a `None` special case.

## Discovery

Enumerate installed packs via the `activegraph.packs` entry point group. Cached per process; call `clear_discovery_cache()` to force a re-scan.

Find a discovered pack by name.

Resolves against the `discover()` scan of installed entry-point packs and returns the :class:`Pack` object — pass it to `runtime.load_pack` to actually load it. Raises `PackNotFoundError` (a `LookupError`) naming the installed packs when the name doesn't resolve.

Reset the cached entry-point scan.

`discover()` memoizes its result for the life of the process (installed packages don't change under a normal run). Tests that install or mock packages dynamically call this between cases; normal usage never needs it.

Scan a directory of `*.md` prompt files with TOML frontmatter.

Each file MUST start with:

```text
---
version = "1.0.0"
name = "optional_name"   # defaults to filename without .md
---
<body>
```

Returns a tuple of `PackPrompt` sorted by name. Content hash is computed over the body (everything after the second `---` line and one separating newline), exactly as it will appear at runtime.

Errors

- missing/malformed frontmatter -> PackPromptLoadError
- missing required `version` field -> PackPromptLoadError
- duplicate prompt name -> PackPromptLoadError
- I/O failure -> PackPromptLoadError

A pack discovered via Python entry points but not yet loaded.

What `discover()` yields per installed distribution that registers the `activegraph.packs` entry-point group: the pack's `name` / `version`, the entry-point string it came from, and the :class:`Pack` object itself. Discovery imports the pack module but changes no runtime state — loading is a separate, explicit `runtime.load_pack` call.

## Approvals + policies

Declared write/tool allowlists for a behavior.

`behavior` names the behavior the lists scope to; the `can_*` fields enumerate the object types, relation types, patches, and tool names it may touch, and `requires_approval` routes matching writes through the pending-approval flow. v0 semantics stand: fields are recorded with the run for audit; the actively enforced gate today is approval routing (pack policies, CONTRACT v0.9), with broader enforcement reserved for a hardening pass.

An object creation that's gated behind a policy approval.

The `id` is unique within the runtime instance and is reused as the eventual object id once approved. `kind` is "object" in v0.9; the field exists so v1.0 can extend it to relations or patches without breaking the API.
