Context
For compliance attestations (HIPAA/NIST/CMMC), knowing which version of a rule pack produced a decision is mandatory metadata — auditors will ask. Today the four compliance packs are anonymous directories: RulePackLoader.discover/load (src/fathom/packs.py, 67 lines) only resolves a directory path from the fathom.packs entry point and loads the templates/ modules/ functions/ rules/ subdirs. No pack-level version, source citation, or maintainer is read anywhere, and AuditRecord.metadata (src/fathom/models.py:364) carries no pack identity.
Strong existing precedent (reuse it, don't reinvent): The SSVC pack already implements exactly this pattern via SSVC_META in src/fathom/rule_packs/ssvc/__init__.py — a dict with version, source, source_sha256, branches_source — exposed as a module attribute, asserted as an ssvc_meta fact that survives an evaluate() round-trip (tests/rule_packs/test_ssvc.py), with SHA-pinned provenance and a documented version-bump discipline. This issue is about generalizing that pattern to the other four shipped packs and surfacing it through the loader, CLI, and audit path. Prefer a Python __manifest__/PACK_META module attribute (consistent with SSVC) over inventing a separate pack.yaml.
Note: rule YAML files already carry a top-level version: "1.0" (read into RulesetDefinition.version), but it is not surfaced at the pack level.
This is distinct from the signed-bundle distribution work in "[P3] Hot-reload + signed bundle distribution + impact analysis" (issue #82): this is in-tree pack identity/provenance, not artifact signing/distribution.
Task
- Define a small validated pack-metadata model (Pydantic) with
name, version (semver), description, controls/sources, maintainer, modeled on the existing SSVC_META shape.
- Have
RulePackLoader read and validate the metadata when loading a pack and expose it on the loaded engine (or return it from the loader).
- Backfill metadata for the four shipped packs (owasp_agentic, nist_800_53, hipaa, cmmc), matching the SSVC convention. Note
owasp_agentic/__init__.py is currently docstring-only (no get_* functions), unlike the other three — bring it in line.
- Surface installed pack names + versions via the CLI. The existing
info command takes a directory path and lists constructs (src/fathom/cli.py:303), so a new packs command that enumerates the fathom.packs entry points + versions is cleaner than overloading info.
- Include pack name+version in
AuditRecord.metadata so attestations cite the exact pack release.
Where
src/fathom/packs.py — metadata discovery/validation in RulePackLoader.
src/fathom/rule_packs/ssvc/__init__.py — reference pattern (SSVC_META); do not regress.
src/fathom/rule_packs/{owasp_agentic,nist_800_53,hipaa,cmmc}/__init__.py — backfill metadata.
src/fathom/cli.py — new packs command (and/or info augmentation).
src/fathom/models.py — metadata model; ensure pack id flows into AuditRecord.metadata.
tests/test_packs.py (new) — loader/validation tests; the per-pack tests live in tests/test_{owasp,nist,hipaa,cmmc}_pack.py and tests/rule_packs/test_ssvc.py.
Acceptance criteria
Size: M
Context
For compliance attestations (HIPAA/NIST/CMMC), knowing which version of a rule pack produced a decision is mandatory metadata — auditors will ask. Today the four compliance packs are anonymous directories:
RulePackLoader.discover/load(src/fathom/packs.py, 67 lines) only resolves a directory path from thefathom.packsentry point and loads thetemplates/ modules/ functions/ rules/subdirs. No pack-level version, source citation, or maintainer is read anywhere, andAuditRecord.metadata(src/fathom/models.py:364) carries no pack identity.Strong existing precedent (reuse it, don't reinvent): The SSVC pack already implements exactly this pattern via
SSVC_METAinsrc/fathom/rule_packs/ssvc/__init__.py— a dict withversion,source,source_sha256,branches_source— exposed as a module attribute, asserted as anssvc_metafact that survives anevaluate()round-trip (tests/rule_packs/test_ssvc.py), with SHA-pinned provenance and a documented version-bump discipline. This issue is about generalizing that pattern to the other four shipped packs and surfacing it through the loader, CLI, and audit path. Prefer a Python__manifest__/PACK_METAmodule attribute (consistent with SSVC) over inventing a separatepack.yaml.Note: rule YAML files already carry a top-level
version: "1.0"(read intoRulesetDefinition.version), but it is not surfaced at the pack level.This is distinct from the signed-bundle distribution work in "[P3] Hot-reload + signed bundle distribution + impact analysis" (issue #82): this is in-tree pack identity/provenance, not artifact signing/distribution.
Task
name,version(semver),description,controls/sources,maintainer, modeled on the existingSSVC_METAshape.RulePackLoaderread and validate the metadata when loading a pack and expose it on the loaded engine (or return it from the loader).owasp_agentic/__init__.pyis currently docstring-only (noget_*functions), unlike the other three — bring it in line.infocommand takes a directory path and lists constructs (src/fathom/cli.py:303), so a newpackscommand that enumerates thefathom.packsentry points + versions is cleaner than overloadinginfo.AuditRecord.metadataso attestations cite the exact pack release.Where
src/fathom/packs.py— metadata discovery/validation inRulePackLoader.src/fathom/rule_packs/ssvc/__init__.py— reference pattern (SSVC_META); do not regress.src/fathom/rule_packs/{owasp_agentic,nist_800_53,hipaa,cmmc}/__init__.py— backfill metadata.src/fathom/cli.py— newpackscommand (and/orinfoaugmentation).src/fathom/models.py— metadata model; ensure pack id flows intoAuditRecord.metadata.tests/test_packs.py(new) — loader/validation tests; the per-pack tests live intests/test_{owasp,nist,hipaa,cmmc}_pack.pyandtests/rule_packs/test_ssvc.py.Acceptance criteria
owasp_agenticno longer diverges from the other packs' module shape.fathom packscommand lists installed pack names + versions.AuditRecord.metadatacites the pack name+version.uv run pytest,uv run ruff check src/ tests/, anduv run mypy src/pass.Size: M