[AVAILABLE],
python/, v1.0.5. 64 passing tests, 0 placeholders. Three gaps found in a
full SDK audit (no bearer-key auth; two real error-taxonomy bugs; a PolicyApi.validate() bug)
were closed and re-verified in that pass, including against a real running local server. A later
pass added test_quickstart_example.py, proving the documented quickstart script itself runs
(see Changelog). See the fix history at the bottom of this page.Install
py.typed is shipped, this is a PEP 561 typed distribution, confirmed by installing into
a clean venv and checking parmana.__file__’s directory for the marker.
Bearer-key authentication
ParmanaClient(api_key=...) is sent as Authorization: Bearer <api_key> on every request, set
once on the underlying requests.Session():
api_key is optional, for the same reason it’s optional server-side: local development against a
server started with PARMANA_AUTH_DISABLED=true needs no key. Every real deployment requires
one — an omitted or wrong key gets a real 401, raised as AuthenticationError (see below), not
a silent failure. See Authentication for how keys are minted.
Models are generated, not hand-maintained
Every model inpython/parmana/models/*.py is generated directly from the TypeScript AST
of packages/shared/src/domain/*.ts (and CryptoAlgorithms.ts) by
python/scripts/generate_models.ts, not hand-aligned copies. A drift guard
(npm run check:python-models, wired into CI) regenerates into memory and fails the build
if the committed output would change. Spot-checked field-for-field against the real JSON
schemas this pass (Authority.display_name optional, Authorization.expires_at optional,
BusinessTransactionMetadata correctly optional-except-business_transaction_id,
ExecutionTrustRecord.settlement_confirmations optional) — all accurate, no drift found.
Enums are real Python str, Enum classes (e.g. SignatureAlgorithm,
VerificationStatus), not bare strings.
Errors, correctly mapped to real conditions
All inherit
ParmanaHttpError → ApiError. Proven against the real HttpTransport (not a
test double) using responses-mocked HTTP built from real, verified response shapes
(python/tests/test_http_transport.py), and against an actual running server in
python/tests/test_live_server_integration.py.
The client also reuses a requests.Session() (connection pooling) and retries idempotent
GETs with backoff on 502/503/504, POSTs are never retried.
Every endpoint the API exposes has a method
client.transactions.create() was missing entirely before this pass — a real capability
(POST /transactions, a second independent entry point into the identical execution pipeline as
execute(), differing only in its 201 status code) with zero SDK coverage. policy.validate
takes (policy_id, policy_version), not a policy document, matching what the route actually
reads.
policy.validate() bug fixed this pass. POST /policies/validate never uses the shared
{error, code?} envelope at 400/404: every status it returns (200, 400, 404) is
{valid, errors}, the caller’s answer, not an SDK-level failure. Before this pass, an unknown
policy raised NotFoundError instead of returning {"valid": False, "errors": [...]} as
documented. 401 is not exempted: it’s generated by caller-auth middleware before this route’s
own handler runs, using the shared envelope like every other route. See
Error handling.Test suite
Previously 26 tests, real but incomplete (no auth coverage, wrong assumption baked into the403
test, no real-server integration test). Now 64, pytest, confirmed against a real run this pass:
unit tests for every error-mapping case including the two fixed bugs, bearer-key header
attachment tests, and two real-server integration suites. test_live_server_integration.py
spawns the actual @parmana/api process (npx tsx packages/api/src/server.ts, the same entry
point npm run dev runs) on a real OS-assigned TCP port and drives it with the real
ParmanaClient over real HTTP: a real 401, a real 403, a real 400, a real policy
rejection, a real 404, a real 409 duplicate, and the policy.validate() 404 case, each
asserted against the exact typed result and exact message the server returned.
test_quickstart_example.py does the same for run_quickstart(), the quickstart example script
itself, additionally asserting its documented printed output still matches what it actually
prints (see Changelog).
ruff/black/mypy were not reverified in this pass — the dev-tooling extras were not
installed in this environment. pytest (48/48) and live-server verification were run directly;
the type/lint claim from an earlier pass is not repeated here as current.Next
TypeScript SDK
The other maintained SDK — same bearer-key model, same error taxonomy shape.
Error catalog
Every error the real API returns, independent of any SDK.