Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,69 @@

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

## capacium-models v0.4.0 — 5-State Trust Model + TRUST_BADGES (2026-06-03)

### Breaking Changes

- **Trust model migrated from 4 states to 5 states**: `TrustState` enum is
now `discovered → pending_review → verified → signed → deprecated`. The
`AUDITED` state has been **removed** and renamed to `PENDING_REVIEW`.
Legacy values continue to be mapped via `LEGACY_STATE_MAP` (`"audited"`
→ `"pending_review"`). Downstream code referencing `TrustState.AUDITED`
directly must be updated to `TrustState.PENDING_REVIEW`. Database rows
with `trust_state = 'audited'` are normalized transparently by
`normalize_legacy_state()`.

- **`VALID_TRANSITIONS` updated** to reflect the 5-state graph:
`discovered → pending_review`, `pending_review → {verified, discovered}`,
`verified → {signed, pending_review}`, `signed → {verified, deprecated}`,
`deprecated → pending_review`. The new `DEPRECATED` end-of-life state
supports superseded or abandoned listings.

### New Features

- **`TRUST_BADGES`** (`src/capacium_models/trust.py`): single source of
truth dict mapping each v2 state to its display label, symbol, color,
description, and ordering. Eliminates vocabulary drift across surfaces
(CLI, MCP, WP bridge, Exchange) (FIX-F-001).

- **`get_trust_badge(trust_state: str)`** helper: returns the badge
definition for any trust state, applying legacy normalization on the
way (`audited`/`indexed` → `pending_review` badge; `claimed` → `verified`
badge). Unknown states fall back to `discovered` badge.

### Bug Fixes

- **`datetime.utcnow()` removed**: replaced 6 occurrences in
`src/capacium_models/models.py` (lines 74, 165, 215, 289, 326, 352) with
`datetime.now(timezone.utc)`. Closes the FIX-G-004 completion gap that
was missed in the original Stream G codemod (the Fix-PRD claimed
zero `utcnow()` across all 6 repos, but capacium-models was overlooked).
Eliminates `DeprecationWarning` noise under Python 3.12+ and prevents
the future Python release that removes `utcnow()` from breaking the
package.

- **Migration 0014 trust-state remap docs** (FIX-F-002): the
`migrations/0014_trust_5state.sql` in capacium-exchange now carries a
pre/post distribution comment block documenting the legacy state remap
(`audited → pending_review`, `claimed → verified`). Verified idempotent
and reversible. Tracked in capacium-exchange — included here as the
cross-repo context for this release.

### Test Migration

- `tests/test_models.py` migrated from v1 4-state vocabulary to v2
5-state vocabulary (19 `TrustState.AUDITED` references → `PENDING_REVIEW`;
10 string `"audited"` → `"pending_review"`; ordering test length
4 → 5; method names renamed for clarity). All 55 tests pass.

### Process Notes

- The 5-state migration commit (`62670bd`) landed on `main` without a
PR (direct push). This release is the first tag covering those changes;
the migration is captured in this CHANGELOG entry rather than a
separate v0.3.0 to keep the release trail aligned with shipped tags.

## capacium-models v0.2.0 — Trust Model v2 (2026-05-24)

### Breaking Changes
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "capacium-models"
version = "0.2.0"
version = "0.4.0"
description = "Shared domain models for the Capacium ecosystem — Listing, TrustState, TrustMachine, and more"
authors = [{name = "Capacium"}]
requires-python = ">=3.10"
Expand Down
14 changes: 7 additions & 7 deletions src/capacium_models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import json
import uuid
from dataclasses import dataclass, field, asdict
from datetime import datetime
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Dict, List, Optional

Expand Down Expand Up @@ -71,7 +71,7 @@ class TrustTransition:

def __post_init__(self):
if not self.timestamp:
self.timestamp = datetime.utcnow().isoformat()
self.timestamp = datetime.now(timezone.utc).isoformat()

def to_dict(self) -> Dict[str, Any]:
return asdict(self)
Expand Down Expand Up @@ -162,7 +162,7 @@ class Listing:
def __post_init__(self):
if not self.id:
self.id = str(uuid.uuid4())
now = datetime.utcnow().isoformat()
now = datetime.now(timezone.utc).isoformat()
if not self.discovered_at:
self.discovered_at = now
if not self.updated_at:
Expand Down Expand Up @@ -212,7 +212,7 @@ class Publisher:
def __post_init__(self):
if not self.id:
self.id = str(uuid.uuid4())
now = datetime.utcnow().isoformat()
now = datetime.now(timezone.utc).isoformat()
if not self.created_at:
self.created_at = now
if not self.updated_at:
Expand Down Expand Up @@ -286,7 +286,7 @@ class Collection:
def __post_init__(self):
if not self.id:
self.id = str(uuid.uuid4())
now = datetime.utcnow().isoformat()
now = datetime.now(timezone.utc).isoformat()
if not self.created_at:
self.created_at = now
if not self.updated_at:
Expand Down Expand Up @@ -323,7 +323,7 @@ def __post_init__(self):
if not self.id:
self.id = str(uuid.uuid4())
if not self.submitted_at:
self.submitted_at = datetime.utcnow().isoformat()
self.submitted_at = datetime.now(timezone.utc).isoformat()

def to_dict(self) -> Dict[str, Any]:
return asdict(self)
Expand All @@ -349,7 +349,7 @@ def __post_init__(self):
if not self.id:
self.id = str(uuid.uuid4())
if not self.verified_at:
self.verified_at = datetime.utcnow().isoformat()
self.verified_at = datetime.now(timezone.utc).isoformat()

def to_dict(self) -> Dict[str, Any]:
return asdict(self)
Expand Down
Loading