Specifications
What this SDK must do, stated so that a test can name it.
The first requirements were extracted from the implementation on 2026-08-15, not written
ahead of it. That direction matters when reading them: each one says what the code already
does and what consumers therefore depend on. Where the extraction found the published
documentation disagreeing with the code, the code won and docs/ was corrected — the
disagreements are listed at the end of this file.
Since then a requirement may also be written ahead of the code, marked planned
(ADR-0002): a line
**Status:** planned under its heading, no @verifies tag yet, and the status line removed
in the change that lands the code and its tests. A requirement with no status line is
implemented. A planned one is a design commitment, not something a consumer may call yet.
Twelve repositories import celine.sdk.auth and ten import celine.sdk.settings
(the companion's knowledge). A requirement here is therefore
a promise to those repositories, and changing one is a platform change, not a local one.
| Document | Covers |
|---|---|
| Configuration | celine.sdk.settings — environment, YAML overlay, interpolation |
| Identity | celine.sdk.auth — token verification, claims, token providers |
| Policy evaluation | celine.sdk.policies — bundle loading, decisions, decision cache |
| Messaging | celine.sdk.broker — MQTT lifecycle, topics, dispatch |
| REC registry client | celine.sdk.rec_registry — the batch asset lookups; the meter writes; the profile write; the area and topology writes, the area rename and the community read; the per-field member writes, the delivery-point writes and the duplicate delivery points read; self-service refusals |
| Onboarding client | celine.sdk.onboarding — the registry sync of a community's areas |
| Digital Twin client | celine.sdk.dt — no refusal detail in any log; no mutable defaults, the payload is the caller's; no legacy grid wind/heat calls |
| Deployment posture | celine.sdk.posture — the dev-only relaxation, the startup guard and the API documentation switch |
| Access audit | celine.sdk.audit — one record of who read what and who was refused, without personal data |
| Spec management | the CLI, spec versioning, generated-client conversion |
Identifiers
REQ- followed by four digits, the harness default. A test declares what it covers with
@verifies REQ-#### in its docstring, and the mapping between the two is generated —
never written by hand. the companion's testing playbook states how.
Numbers are allocated in blocks per document so a new requirement can be appended without
renumbering: configuration 0001–0019, identity 0020–0049, policy evaluation
0050–0069, messaging 0070–0099, spec management 0100–0119, REC registry client
0120–0139, onboarding client 0140–0159, Digital Twin client 0160–0179, deployment posture 0180–0189, access audit 0190–0209.
What is deliberately not specified here
- The generated tree.
src/celine/sdk/openapiis 454 files produced bytask gen; requiring anything of it would be requiring something of its generator. What is specified is the conversion boundary around it (REQ-0110, REQ-0111) and the versioning that feeds it. - The service APIs themselves. Those belong to the services. This SDK requires only that a spec is fetched, versioned and turned into a client.
- Compatibility with the repositories that import this one. Nothing in this repository can answer it.
Where the documentation disagreed with the code
Found while extracting, and corrected in docs/ in the same change. Listed because a
consumer may have written code against the documented version:
| Documented | Actual |
|---|---|
OIDC_ISSUER, MQTT_HOST, POLICIES_URL env vars |
CELINE_OIDC_BASE_URL, CELINE_MQTT_HOST; no POLICIES_URL exists (REQ-0001) |
OidcTokenProvider(settings) |
OidcClientCredentialsProvider(base_url=…, client_id=…, client_secret=…) (REQ-0037) |
from celine.sdk.auth import StaticTokenProvider |
it was not exported there — the export was added rather than the document changed (REQ-0035) |
AccessToken carrying expires_in, scope, a datetime expiry |
access_token, expires_at (epoch float), refresh_token, token_type (REQ-0034) |
| MQTT username is the client id, password the token | username is the token, password the literal jwt (REQ-0080) |
BrokerProtocol, MqttMessage, broker.stats() |
Broker, BrokerMessage/ReceivedMessage, get_stats() (REQ-0090) |
| "existing spec versions are not overwritten" | a re-fetch of the same info.version overwrites it (REQ-0104) |
Three of these were fixed in the code rather than absorbed into the documentation, because
the documented behaviour was the right one: StaticTokenProvider and PoliciesSettings are
now exported where they were documented to be, and version ordering is numeric (REQ-0105).