# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.2.21] - 2026-04-20

### Changed

- Remove backward compat for `  - key: value` relationship field format
- Only accept `  key: value` format for relationship fields
- Refactor writeback.rs: extract `insert_front_matter_id`, `insert_body_id`, `strip_id_prefix`, `append_involved_section`
- Fix duplicate ID insertion bug for body elements (relationships, timeline edges, etc.)

## [0.2.20] - 2026-04-20

### Fixed

- Writeback replaces empty `id:` field instead of inserting duplicate

## [0.2.19] - 2026-04-20

### Fixed

- Include cases without `id:` in case index for related case resolution

## [0.2.18] - 2026-04-20

### Added

- Add `pardoned` status to `CaseStatus` enum

## [0.2.17] - 2026-04-20

### Changed

- Render aliases as inline comma-separated text instead of `<ul>/<li>`
- Render entity URLs as domain-only link text with `·` separators instead of `<ul>/<li>`

## [0.2.16] - 2026-04-20

### Fixed

- Fix multi-line comma-separated list field continuation (e.g. aliases spanning multiple lines)

### Added

- Render entity URLs as clickable links in HTML output (`<div class="loom-urls">`)

## [0.2.15] - 2026-04-19

### Added

- Deterministic ID writeback for `preceded_by`, `involved_in`, and `related_to` relationships
- `## Involved` section parsing and creation in case files
- `## Timeline` bullet format parser with `id:` field support (replaces chain format)
- `TimelineEdge` and `InvolvedIn` writeback kinds
- Writeback round-trip tests for all new relationship types

### Changed

- Timeline parser only accepts bullet format (`- A -> B`); chain format removed
- `InvolvedIn` writeback carries entity name for automatic section creation

## [0.2.14] - 2026-04-19

### Fixed

- Fix double-hashing of already-rewritten thumbnail URLs in HTML renderer

## [0.2.13] - 2026-04-19

### Added

- `generate_html_output()` public API in lib.rs for reuse by loom-seed

## [0.2.12] - 2026-04-19

### Added

- Structured `AmountEntry` type replacing string amount/currency pairs on relationships and case nodes
- `amounts:` DSL field with approximate markers, currency, and labeled entries (bribe, fine, embezzlement, etc.)
- `fraud` amount label
- `related_to` relationship type and `## Related Cases` content DSL section with graph edges
- `CaseIndex` pre-pass for cross-case title/NULID resolution
- `build_case_index()` public API in lib.rs for reuse by loom-seed
- Human-readable number formatting: million/billion/trillion for numbers >= 1M
- Improved HTML: individual amount badges, financial details as definition list, related cases as cards
- HTML rendering for asset, document, and event entity fields
- Country-scoped tag pages, jurisdiction/nationality country name display
- Consistent enum formatting across all HTML sections

### Changed

- Financial details section moved above sources for visibility
- `INVOLVED_IN` relationships now only generated for registry entities (people/organizations), not inline events

### Fixed

- Duplicate `INVOLVED_IN` edges from events to case nodes

## [0.2.11] - 2026-04-19

### Added

- Case as first-class graph node (ADR-019): `slug`, `case_type` fields on Case nodes
- `INVOLVED_IN` relationship type for entity-case connections
- NULID auto-generation and writeback for case files (`nulid:` frontmatter field)
- `index.json` generation mapping NULID to slug for static route resolution
- Slug-based static HTML output URLs
- `data-og-image` attribute in HTML output for OG image meta tags
- Cross-file duplicate relationship ID validation in `validate` command
- `CaseNulid` writeback variant for correct `nulid:` field (not `id:`)

### Fixed

- Case NULID writeback now uses `nulid:` field instead of creating duplicate `id:` field

## [0.2.10] - 2026-04-18

### Changed

- **Breaking:** Rename entity directories: `actors/` → `people/`, `institutions/` → `organizations/`
- **Breaking:** Rename DSL section headings: `## Actors` → `## People`, `## Institutions` → `## Organizations`
- **Breaking:** Rename conflict detection rel types: `DONATED_TO` → `PAID_TO`, `CONTRACTED_WITH` → `AWARDED_CONTRACT`, `LOBBIED_FOR` → `LOBBIED`
- **Breaking:** Rename conflict detection labels: `Actor` → `Person`, `Institution` → `Organization`, `PublicRecord` → `Event`
- Update COI pattern names: "Donation-Appointment Cycle" → "Payment-Appointment Cycle", "Contract Kickback Cycle" → "Contract-Payment Cycle", "Donor Influence Chain" → "Payment Influence Chain"
- Hub detection now matches `Person` → `Organization` pairs (was `Actor` → `Institution`)
- Rewrite DSL.md to reflect all ADR-014 changes (new labels, fields, rel types, enum values)

## [0.2.9] - 2026-04-18

### Added

- Detect duplicate event names across case files in `validate` command
- Verify registry entity (actors/institutions) thumbnail URLs in `verify` command

## [0.2.8] - 2026-04-17

### Fixed

- Fix duplicate ID writeback for shared registry entities when building multiple cases
  (`writeback_registry_entities` now checks if the entity file already has an `id:` in front matter before writing)

## [0.2.7] - 2026-04-17

### Fixed

- Add User-Agent header to weave-image downloads to prevent Wikipedia 403 blocks

### Changed

- Switch from lossless to lossy WebP encoding via `libwebp-sys` (quality 80, retry at 60 if over limit)
- Increase max thumbnail output size from 50 KB to 100 KB

## [0.2.6] - 2026-04-17

### Changed

- Remove S3/thumbnail infrastructure from weave-content (moved to private loom-seed binary)
- Make `download_async` public in weave-image for direct async usage
- Upgrade reqwest 0.12 to 0.13 (feature `rustls-tls` renamed to `rustls`)
- Upgrade sha2 0.10 to 0.11

## [0.2.5] - 2026-04-17

### Added

- Library API for weave-content: `resolve_content_root`, `load_registry`, `resolve_case_files`, `build_case_output` public functions for embedding in other binaries

## [0.2.4] - 2026-04-17

### Fixed

- Remove weave-content from workspace members to fix hex package compilation
  (cargo metadata failed because weave-content source files are not included in the hex package)
- Make weave-content a standalone crate with explicit config instead of workspace inheritance

## [0.2.3] - 2026-04-17

### Fixed

- Fix clippy pedantic errors: wildcard match for single variant, redundant closure, function too many lines
- Remove unused `entries()` method from `EntityRegistry`
- Extract `writeback_registry_entities` and `write_case_output` helpers from `build_single_case`

## [0.2.2] - 2026-04-17

### Added

- **weave-content** crate published to crates.io (`cargo install weave-content`)
  - Content DSL parser, validator, and builder for case files
  - Shared entity registry: standalone actor/institution files with cross-file name resolution
  - NULID write-back: `build` generates IDs and writes them back to source files
  - Auto-discover: finds `actors/`, `institutions/`, `cases/` directories from content root
  - Commands: `validate`, `verify`, `build`

### Fixed

- NEXT relationship IDs no longer written back to timeline section during build
  (timeline chains are `Event A -> Event B` syntax, not bullet-list relationships)

## [0.2.1] - 2026-04-16

### Added

- `thumbnail_key/1` and `process_thumbnail/1` NIF exports in the bridge crate
- `Weave.Image` Elixir module wrapping image processing NIFs

### Changed

- Removed external site references from READMEs

## [0.2.0] - 2026-04-16

### Added

- **weave-image** crate for thumbnail processing
  - Download images from URLs with 5 MB size limit and 15s timeout
  - Center-crop and resize to 256x256 lossless WebP thumbnails
  - Deterministic storage keys via SHA-256 of source URL (`thumbnails/{hex[0..32]}.webp`)
  - Output size capped at 50 KB
  - `thumbnail_key/1`, `download/1`, `resize_to_webp/1`, `process_thumbnail/1`
  - Unit tests for resize, key generation, size limits, error cases

- Per-crate README files for crates.io

## [0.1.0] - 2026-04-16

### Added

- **weave-graph** crate for conflict-of-interest pattern detection on knowledge graphs
  - `Node` and `Edge` types for subgraph representation (NULID-based identifiers)
  - `Subgraph` and `IndexedSubgraph` with O(1) adjacency list lookups
  - Cycle detection -- enumerate directed simple cycles matching edge-type sequences
  - Path detection -- constrained DFS matching edge types and node labels against templates
  - Hub detection -- flag Actor-Institution pairs with concentrated influence edges
  - Six built-in patterns: COI-001 through COI-006
  - Configurable `DetectOpts` with max depth and pattern ID filtering
  - Property-based tests via `proptest` for correctness guarantees
  - Performance benchmarks: <50ms for 10K nodes / 50K edges

- **weave_nif** crate providing Rustler NIF bridge
  - `health_check/0` -- verify NIF is loaded
  - `detect_conflicts/2` -- JSON-in, JSON-out conflict detection on dirty CPU scheduler

- **rbt_weave** Elixir hex package
  - `Weave.Native` -- low-level NIF bindings via Rustler
  - `Weave.Graph` -- public API delegating to `Weave.Native`
  - Publishable to both [crates.io](https://crates.io) (Rust) and [hex.pm](https://hex.pm) (Elixir)

[Unreleased]: https://github.com/redberrythread/weave/compare/v0.2.8...HEAD
[0.2.8]: https://github.com/redberrythread/weave/compare/v0.2.7...v0.2.8
[0.2.7]: https://github.com/redberrythread/weave/compare/v0.2.6...v0.2.7
[0.2.6]: https://github.com/redberrythread/weave/compare/v0.2.5...v0.2.6
[0.2.5]: https://github.com/redberrythread/weave/compare/v0.2.4...v0.2.5
[0.2.4]: https://github.com/redberrythread/weave/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/redberrythread/weave/compare/v0.2.2...v0.2.3
[0.2.2]: https://github.com/redberrythread/weave/compare/v0.2.1...v0.2.2
[0.2.1]: https://github.com/redberrythread/weave/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/redberrythread/weave/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/redberrythread/weave/releases/tag/v0.1.0
