cleanlib-sdk 0.4.4__tar.gz → 0.4.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. cleanlib_sdk-0.4.6/CHANGELOG.md +82 -0
  2. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/PKG-INFO +1 -1
  3. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/__init__.py +7 -1
  4. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/types.py +22 -0
  5. cleanlib_sdk-0.4.6/cleanlib_sdk/verdict_to_envelope.py +156 -0
  6. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/pyproject.toml +1 -1
  7. cleanlib_sdk-0.4.4/CHANGELOG.md +0 -41
  8. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/.gitignore +0 -0
  9. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/README.md +0 -0
  10. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/client.py +0 -0
  11. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/derive_status.py +0 -0
  12. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/errors.py +0 -0
  13. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/http/__init__.py +0 -0
  14. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/http/enrich_client.py +0 -0
  15. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/http/enrich_types.py +0 -0
  16. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/http/remediation_client.py +0 -0
  17. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/http/types.py +0 -0
  18. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/http/verdict_client.py +0 -0
  19. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/reason_codes.py +0 -0
  20. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/schema/__init__.py +0 -0
  21. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/schema/verdict_envelope_v1.py +0 -0
  22. {cleanlib_sdk-0.4.4 → cleanlib_sdk-0.4.6}/cleanlib_sdk/transport.py +0 -0
@@ -0,0 +1,82 @@
1
+ # cleanlib-sdk changelog
2
+
3
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
4
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
+
6
+ ## [0.4.6] — 2026-06-05
7
+
8
+ ### Added
9
+
10
+ - `cleanlib_sdk.verdict_to_envelope_v1(verdict)` — pure-function structural
11
+ adapter that converts the App's wire-shape `Verdict` to the canonical
12
+ customer-facing `VerdictEnvelopeV1` (returned as a `dict[str, Any]`
13
+ matching `VerdictEnvelopeV1Schema`). Second of four parity-locked
14
+ implementations across the SDK fleet (Rust: `cleanlib_client::verdict_to_envelope_v1`
15
+ shipped in v0.1.4; JS + Go to follow). Ratified at
16
+ `decisions/2026-06-05-cycle-14-cleanlib-48-envelope-shape-ratification.md`
17
+ (cleanlib monorepo).
18
+ - Status/reason_code derivation table mirrors the Rust adapter byte-for-byte:
19
+ `VECTOR_VERDICT + CRITICAL → DENY + VERDICT_EXPLOITATION_CRITICAL`,
20
+ `+ HIGH → DENY + VERDICT_LOW_TRUST`,
21
+ `+ MEDIUM → WARN + VERDICT_ABANDONED`,
22
+ `DM_THRESHOLD_BLOCK + HIGH/CRITICAL → DENY + VERDICT_DENY_LIST`,
23
+ `+ MEDIUM → WARN + VERDICT_DENY_LIST`,
24
+ `ALLOWED_NO_FINDINGS / INSUFFICIENT_DATA / unknown → ALLOW + VERDICT_CLEAN`.
25
+ `stale_since_at` set overrides `reason_code` to `VERDICT_DEGRADED_STALE`
26
+ while preserving the status tier.
27
+ - `rich_data` sub-object populated with `suggested_actions`, `similar_to`,
28
+ `evidence_gaps`, `previous_verdict`, `confidence`, `composite_score`,
29
+ `source`, `severity`, `staleness_reason` when present on the Verdict.
30
+ - Enrich-cascade slots (`remediation`, `exploitability`, `availability`)
31
+ are left `None`; consumers populate them via `HttpEnrichClient` before
32
+ calling `derive_status` for the canonical (status, reason_code) decision.
33
+
34
+ ## [0.4.5] — 2026-06-04
35
+
36
+ ### Added
37
+
38
+ - `Verdict.previous_verdict` field plus a new `PreviousVerdict` pydantic
39
+ model (`verdict_id`, `verdict`, `computed_at`, `diff`). Parity-ripple with
40
+ `sdk-go.PreviousVerdict`. The CleanLibrary App emits this field in its
41
+ `/v1/customer/verdicts/...` envelope when a prior verdict exists for the
42
+ same package version; previously sdk-py deserialized it as a no-op via
43
+ pydantic-default tolerance and customers lost the verdict-state-change
44
+ signal. `PreviousVerdict` is exported from the package root alongside
45
+ `Verdict`.
46
+
47
+ ## [0.4.4] — 2026-06-04
48
+
49
+ ### Fixed
50
+
51
+ - `cleanlib_sdk.__version__` synchronized with the package manifest version.
52
+ Previously the in-code `__version__` reported `0.4.2` while the published
53
+ artifact was `0.4.3` — telemetry that relied on `__version__` for User-Agent
54
+ composition was under-reporting deployment penetration.
55
+ - Wheel + sdist now include `CHANGELOG.md`. Customers running `pip download`
56
+ + tar inspection (or browsing the PyPI release files page) see the version
57
+ history alongside the package code.
58
+
59
+ ## [0.4.3] — 2026-06-03
60
+
61
+ ### Fixed
62
+
63
+ - `pyproject.toml` `[project.urls]` table sanitized: `Homepage`,
64
+ `Documentation`, and `Bug Tracker` now point at `cleanlibrary.clnstrt.dev`
65
+ rather than the internal source-of-truth URLs that previously rendered on
66
+ the PyPI listing page.
67
+ - `README.md` cross-references brand-clean: repo-internal links replaced with
68
+ the customer-facing `cleanlibrary.clnstrt.dev` documentation site and
69
+ cross-language SDK registry pages (crates.io, pkg.clnstrt.dev, npmjs.com).
70
+
71
+ ### Notes
72
+
73
+ - No runtime API or wire-protocol changes; this is a metadata + docs ship.
74
+ - Verified via `python -m build` against the resulting `cleanlib_sdk-0.4.3-py3-none-any.whl`
75
+ `METADATA` + `cleanlib_sdk-0.4.3.tar.gz` `PKG-INFO`: zero
76
+ internal-source URL references.
77
+
78
+ ## 0.4.2 — enrich verb cascade + F4 default-flip to cleanlib-enrich
79
+
80
+ ## 0.4.1 — per-domain HTTP client split (HttpRemediationClient) + schema/reason-codes mirror + DeriveStatus + Client @deprecated
81
+
82
+ ## 0.4.0 — verb cascade + rich-data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cleanlib-sdk
3
- Version: 0.4.4
3
+ Version: 0.4.6
4
4
  Summary: CleanLibrary Python SDK — HttpVerdictClient + HttpRemediationClient + HttpEnrichClient triad (sister-shape with @cleanstart/cleanlib-sdk v0.4.4)
5
5
  Project-URL: Homepage, https://cleanlibrary.clnstrt.dev
6
6
  Project-URL: Documentation, https://cleanlibrary.clnstrt.dev/docs/sdk-python
@@ -92,6 +92,7 @@ from cleanlib_sdk.types import (
92
92
  PolicyPreviewResponse,
93
93
  PolicyPreviewResult,
94
94
  RecommendedVersion,
95
+ PreviousVerdict,
95
96
  RiskAcceptResponse,
96
97
  ScanResponse,
97
98
  ScanResult,
@@ -99,7 +100,9 @@ from cleanlib_sdk.types import (
99
100
  Verdict,
100
101
  )
101
102
 
102
- __version__ = "0.4.4"
103
+ from cleanlib_sdk.verdict_to_envelope import verdict_to_envelope_v1
104
+
105
+ __version__ = "0.4.6"
103
106
 
104
107
  __all__ = [
105
108
  # v0.4.2 per-domain HTTP client triad (primary API)
@@ -142,8 +145,11 @@ __all__ = [
142
145
  "AVAILABILITY_ENUM",
143
146
  "derive_status",
144
147
  "StatusResult",
148
+ # CLEANLIB-48 envelope-shape ratification (v0.4.6)
149
+ "verdict_to_envelope_v1",
145
150
  # cycle-6 v0.4.0 types
146
151
  "Verdict",
152
+ "PreviousVerdict",
147
153
  "Attestation",
148
154
  "SignedAttestation",
149
155
  "RecommendedVersion",
@@ -110,6 +110,28 @@ class Verdict(BaseModel):
110
110
  # Cycle-6 9th field (App PR #105). Package-level vuln/risk summary.
111
111
  package_risk: Optional["PackageRisk"] = None
112
112
 
113
+ # v0.4.5 CLEANLIB-50: previous_verdict parity with sdk-go.PreviousVerdict.
114
+ # CleanLibrary App emits this when a prior verdict exists for the same
115
+ # (ecosystem, package, version); previously sdk-py deserialized it as a
116
+ # no-op via pydantic-default tolerance and customers lost the
117
+ # verdict-state-change signal.
118
+ previous_verdict: Optional["PreviousVerdict"] = None
119
+
120
+
121
+ class PreviousVerdict(BaseModel):
122
+ """Prior-verdict comparison surfaced on the canonical Verdict envelope
123
+ when a prior verdict exists for the same package version. Sister-shape
124
+ with `cleanlib_sdk_go.PreviousVerdict` + `cleanlib_client::PreviousVerdict`
125
+ + `cleanlib_core::PreviousVerdict` in the App.
126
+ """
127
+
128
+ model_config = ConfigDict(populate_by_name=True)
129
+
130
+ verdict_id: Optional[str] = None
131
+ verdict: Optional[str] = None
132
+ computed_at: Optional[str] = None
133
+ diff: Optional[str] = None
134
+
113
135
 
114
136
  class RecommendedVersion(BaseModel):
115
137
  """Recommended upgrade target. Mirrors cleanlib_core::RecommendedVersion;
@@ -0,0 +1,156 @@
1
+ """verdict_to_envelope_v1 — Verdict (App wire) → VerdictEnvelopeV1 (canonical) adapter.
2
+
3
+ Ratified at ``decisions/2026-06-05-cycle-14-cleanlib-48-envelope-shape-ratification.md``
4
+ (in the cleanlib monorepo). Sister of:
5
+
6
+ - ``cleanlib_client::verdict_to_envelope_v1`` (Rust)
7
+ - ``verdictToEnvelopeV1`` (sdk-js, planned)
8
+ - ``VerdictToEnvelopeV1`` (sdk-go, planned)
9
+
10
+ All four implementations produce byte-identical output for the same input
11
+ Verdict; this is verified by the ``cleanlib-contract-fixtures`` Verdict-to-
12
+ envelope golden pack (planned, decision doc action D.1).
13
+
14
+ The adapter is a PURE STRUCTURAL CONVERSION. The output envelope's
15
+ ``remediation`` / ``exploitability`` / ``availability`` slots are always
16
+ ``None``; consumers populate them via the ``HttpEnrichClient`` cascade
17
+ before running ``derive_status`` for the canonical decision derivation.
18
+
19
+ Status/reason_code mapping table (App's view; consumer may re-derive
20
+ after enriching)::
21
+
22
+ verdict label | severity | status | reason_code
23
+ ----------------------|-----------------|--------|---------------------------------
24
+ VECTOR_VERDICT | CRITICAL | DENY | VERDICT_EXPLOITATION_CRITICAL
25
+ VECTOR_VERDICT | HIGH | DENY | VERDICT_LOW_TRUST
26
+ VECTOR_VERDICT | MEDIUM | WARN | VERDICT_ABANDONED
27
+ VECTOR_VERDICT | LOW / NONE | ALLOW | VERDICT_CLEAN
28
+ DM_THRESHOLD_BLOCK | CRITICAL / HIGH | DENY | VERDICT_DENY_LIST
29
+ DM_THRESHOLD_BLOCK | MEDIUM | WARN | VERDICT_DENY_LIST
30
+ DM_THRESHOLD_BLOCK | LOW / NONE | ALLOW | VERDICT_CLEAN
31
+ ALLOWED_NO_FINDINGS | any | ALLOW | VERDICT_CLEAN
32
+ INSUFFICIENT_DATA | any | ALLOW | VERDICT_CLEAN
33
+ (unknown label) | any | ALLOW | VERDICT_CLEAN
34
+
35
+ Freshness override: if ``verdict.stale_since_at`` is not ``None``, the
36
+ ``reason_code`` is overridden to ``VERDICT_DEGRADED_STALE`` (status tier
37
+ preserved). Sister of the enrich-substrate freshness override applied by
38
+ ``derive_status`` after the enrich cascade lands.
39
+ """
40
+
41
+ from __future__ import annotations
42
+
43
+ from typing import Any
44
+
45
+ from cleanlib_sdk.types import Verdict
46
+
47
+
48
+ def verdict_to_envelope_v1(verdict: Verdict) -> dict[str, Any]:
49
+ """Convert a wire-shape :class:`Verdict` to a :data:`VerdictEnvelopeV1` dict.
50
+
51
+ Returns the envelope as a plain ``dict[str, Any]`` matching the
52
+ ``VerdictEnvelopeV1Schema`` (sdk-js v0.4.1 source-of-truth schema). The
53
+ returned dict can be passed directly to :func:`derive_status` after
54
+ populating the enrich-cascade fields.
55
+ """
56
+ status, reason_code = _derive_initial_status_reason(verdict)
57
+
58
+ # `as_of` falls back: computed_at → data_freshness_at → "". Mirrors
59
+ # Rust adapter's `verdict_to_envelope_v1` behavior exactly.
60
+ as_of = _isoformat_or_empty(verdict.computed_at) or _isoformat_or_empty(
61
+ verdict.data_freshness_at
62
+ )
63
+
64
+ return {
65
+ "status": status,
66
+ "reason_code": reason_code,
67
+ "human_message": verdict.reasoning or "",
68
+ "as_of": as_of,
69
+ "rich_data": _build_rich_data(verdict),
70
+ "remediation": None,
71
+ "exploitability": None,
72
+ "availability": None,
73
+ }
74
+
75
+
76
+ def _derive_initial_status_reason(verdict: Verdict) -> tuple[str, str]:
77
+ # The wire shape carries `verdict_label` (alias for ``verdict``) and
78
+ # ``severity`` as Optional strings. Normalize to upper-case so legacy
79
+ # lower-case fixtures don't misclassify.
80
+ label = (verdict.verdict_label or "").upper()
81
+ severity = (verdict.severity or "NONE").upper()
82
+
83
+ if label == "VECTOR_VERDICT":
84
+ if severity == "CRITICAL":
85
+ status, reason = "DENY", "VERDICT_EXPLOITATION_CRITICAL"
86
+ elif severity == "HIGH":
87
+ status, reason = "DENY", "VERDICT_LOW_TRUST"
88
+ elif severity == "MEDIUM":
89
+ status, reason = "WARN", "VERDICT_ABANDONED"
90
+ else:
91
+ status, reason = "ALLOW", "VERDICT_CLEAN"
92
+ elif label == "DM_THRESHOLD_BLOCK":
93
+ if severity in ("CRITICAL", "HIGH"):
94
+ status, reason = "DENY", "VERDICT_DENY_LIST"
95
+ elif severity == "MEDIUM":
96
+ status, reason = "WARN", "VERDICT_DENY_LIST"
97
+ else:
98
+ status, reason = "ALLOW", "VERDICT_CLEAN"
99
+ else:
100
+ # ALLOWED_NO_FINDINGS / INSUFFICIENT_DATA / unknown → ALLOW + CLEAN.
101
+ # Forward-compat: future App-side labels default to ALLOW so older
102
+ # SDKs don't break on schema additions.
103
+ status, reason = "ALLOW", "VERDICT_CLEAN"
104
+
105
+ if verdict.stale_since_at is not None:
106
+ # Sister of the enrich-substrate freshness override in
107
+ # derive_status: status tier preserved, reason_code overridden.
108
+ reason = "VERDICT_DEGRADED_STALE"
109
+ return status, reason
110
+
111
+
112
+ def _build_rich_data(verdict: Verdict) -> dict[str, Any]:
113
+ """Build the envelope's ``rich_data`` sub-object from the wire-shape
114
+ Verdict's rich fields. Mirrors the Rust adapter's `build_rich_data`."""
115
+ rich: dict[str, Any] = {}
116
+ if verdict.suggested_actions:
117
+ rich["suggested_actions"] = list(verdict.suggested_actions)
118
+ if verdict.similar_to:
119
+ rich["similar_to"] = list(verdict.similar_to)
120
+ if verdict.evidence_gaps:
121
+ rich["evidence_gaps"] = list(verdict.evidence_gaps)
122
+ if verdict.previous_verdict is not None:
123
+ pv = verdict.previous_verdict
124
+ rich["previous_verdict"] = {
125
+ "verdict_id": pv.verdict_id or "",
126
+ "verdict": pv.verdict or "",
127
+ "computed_at": pv.computed_at or "",
128
+ "diff": pv.diff or "",
129
+ }
130
+ rich["confidence"] = verdict.confidence if verdict.confidence is not None else 0.0
131
+ rich["composite_score"] = (
132
+ verdict.composite_score if verdict.composite_score is not None else 0
133
+ )
134
+ if verdict.source:
135
+ rich["source"] = verdict.source
136
+ if verdict.severity:
137
+ rich["severity"] = verdict.severity
138
+ if verdict.staleness_reason:
139
+ rich["staleness_reason"] = verdict.staleness_reason
140
+ return rich
141
+
142
+
143
+ def _isoformat_or_empty(dt: Any) -> str:
144
+ """Render a datetime or ISO string as a UTC ISO string with Z suffix."""
145
+ if dt is None:
146
+ return ""
147
+ if isinstance(dt, str):
148
+ return dt
149
+ # pydantic datetime — use isoformat; convert +00:00 → Z for wire-parity
150
+ # with the Rust adapter which emits whatever the App's serializer
151
+ # produced.
152
+ try:
153
+ s = dt.isoformat()
154
+ except Exception:
155
+ return str(dt)
156
+ return s.replace("+00:00", "Z")
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "cleanlib-sdk"
7
- version = "0.4.4"
7
+ version = "0.4.6"
8
8
  description = "CleanLibrary Python SDK — HttpVerdictClient + HttpRemediationClient + HttpEnrichClient triad (sister-shape with @cleanstart/cleanlib-sdk v0.4.4)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,41 +0,0 @@
1
- # cleanlib-sdk changelog
2
-
3
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
4
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
-
6
- ## [0.4.4] — 2026-06-04
7
-
8
- ### Fixed
9
-
10
- - `cleanlib_sdk.__version__` synchronized with the package manifest version.
11
- Previously the in-code `__version__` reported `0.4.2` while the published
12
- artifact was `0.4.3` — telemetry that relied on `__version__` for User-Agent
13
- composition was under-reporting deployment penetration.
14
- - Wheel + sdist now include `CHANGELOG.md`. Customers running `pip download`
15
- + tar inspection (or browsing the PyPI release files page) see the version
16
- history alongside the package code.
17
-
18
- ## [0.4.3] — 2026-06-03
19
-
20
- ### Fixed
21
-
22
- - `pyproject.toml` `[project.urls]` table sanitized: `Homepage`,
23
- `Documentation`, and `Bug Tracker` now point at `cleanlibrary.clnstrt.dev`
24
- rather than the internal source-of-truth URLs that previously rendered on
25
- the PyPI listing page.
26
- - `README.md` cross-references brand-clean: repo-internal links replaced with
27
- the customer-facing `cleanlibrary.clnstrt.dev` documentation site and
28
- cross-language SDK registry pages (crates.io, pkg.clnstrt.dev, npmjs.com).
29
-
30
- ### Notes
31
-
32
- - No runtime API or wire-protocol changes; this is a metadata + docs ship.
33
- - Verified via `python -m build` against the resulting `cleanlib_sdk-0.4.3-py3-none-any.whl`
34
- `METADATA` + `cleanlib_sdk-0.4.3.tar.gz` `PKG-INFO`: zero
35
- internal-source URL references.
36
-
37
- ## 0.4.2 — enrich verb cascade + F4 default-flip to cleanlib-enrich
38
-
39
- ## 0.4.1 — per-domain HTTP client split (HttpRemediationClient) + schema/reason-codes mirror + DeriveStatus + Client @deprecated
40
-
41
- ## 0.4.0 — verb cascade + rich-data
File without changes
File without changes