TracePulse PLM shared contracts: Kernel API surface, idempotency, capabilities, knowledge envelope, pricing, artifact store, trace context. Frozen in US-W1.0; consumed by US-W1.1+ and US-CR.0+.
Project description
plm-shared
Frozen contracts shared across TracePulse PLM product lines (Core, Skill Kernel, Skill Packages, Knowledge, Workbench, Studio, Connectors). Owned by US-W1.0; consumed by US-W1.1 → US-W1.13 and US-CR.0 → US-CR.13 without modification.
US-W1.0 ships in 7 PRs:
| PR | Scope |
|---|---|
| PR-1 | package skeleton + Pydantic models + IdempotencyKey + Capability |
| PR-2 | Alembic migrations 0001-0007 + SQLAlchemy ORM + db.py |
| PR-3 | migration 0008 (Postgres roles) + append-only audit suite |
| PR-4 | migration 0009 (RLS) + middleware.py + tenant-isolation audit |
| PR-5 | ObjectStoreBackend (S3 wire protocol) + [s3] extra + tests |
| PR-6 | SqlAlchemyKernelEmitter + migration 0011 (pricing dedup) + integ |
| PR-7 | import-linter contracts + BL5 + CI workflow + AC + QA + close |
Surface
| Module | Purpose |
|---|---|
kernel_api |
Pydantic args + KernelEmitter Protocol (frozen) |
sqlalchemy_kernel_emitter |
Concrete SqlAlchemyKernelEmitter impl (PR-6) |
idempotency |
IdempotencyKey + compute_content_hash |
capabilities |
Capability enum + YAML loader |
knowledge_envelope |
KnowledgeEnvelopeV1 frozen schema |
trace_context |
TraceContext ContextVar + W0.6 bridge |
pricing |
compute_cost + CostStatus + PricingSnapshot dataclass |
artifact_store |
ArtifactStorageBackend Protocol + LocalFilesystemBackend + ObjectStoreBackend |
middleware |
PlmTenantMiddleware (ASGI) + parse_core_caller + cors_allowed_headers |
db |
SQLAlchemy 2.0 ORM models + tenant_scoped_session |
Kernel API (US-W1.0 / AC-2)
plm_shared.kernel_api.KernelEmitter is the frozen 4-method
Protocol that every product line uses to record events. The
authorised emitter boundary is Core: product lines submit decisions
to Core (e.g. Workbench routes HITL approvals), and Core is the
authorised emitter through plm-shared.
Protocol
from plm_shared.kernel_api import KernelEmitter
from plm_shared.kernel_api import (
EmitTelemetryArgs,
EmitGovernanceArgs,
EmitLlmCallArgs,
EmitConnectorCallArgs,
)
class KernelEmitter(Protocol):
def emit_telemetry(self, args: EmitTelemetryArgs) -> None: ...
def emit_governance(self, args: EmitGovernanceArgs) -> None: ...
def record_llm_call(self, args: EmitLlmCallArgs) -> None: ...
def record_connector_call(self, args: EmitConnectorCallArgs) -> None: ...
Every method returns None on success — including the silent replay
path (200 OK: same key + same content). A logical-key collision
(same identity, different content) raises
plm_shared.sqlalchemy_kernel_emitter.IdempotencyConflict.
Args
EmitTelemetryArgs(
event_type: str, # e.g. "knowledge_call"
span_id: str,
capability: str, # capability tag for the event
payload: Dict[str, Any] = {}, # event-specific shape
idempotency_key: IdempotencyKey,
)
EmitGovernanceArgs(
governance_event_type: str, # e.g. "hitl_resolved"
span_id: str,
payload: Dict[str, Any] = {},
idempotency_key: IdempotencyKey,
)
EmitLlmCallArgs(
span_id: str,
model: str,
prompt_tokens: int,
completion_tokens: int,
pricing_version: str, # "litellm-live" | "overrides-static" | "unavailable"
cost_eur: Optional[float] = None,
cost_status: str, # "pending" | "calculated" | "unavailable"
idempotency_key: IdempotencyKey,
)
EmitConnectorCallArgs(
span_id: str,
connector_id: str, # e.g. "3dx-rest"
capability: str, # e.g. "read.parts"
envelope: Dict[str, Any] = {},
pricing_version: str,
cost_eur: Optional[float] = None,
cost_status: str,
idempotency_key: IdempotencyKey,
)
Concrete emitter — SqlAlchemyKernelEmitter
from plm_shared.sqlalchemy_kernel_emitter import (
IdempotencyConflict,
SqlAlchemyKernelEmitter,
)
from plm_shared.idempotency import IdempotencyKey, compute_content_hash
from plm_shared.kernel_api import EmitLlmCallArgs
tenant_id = "00000000-0000-0000-0000-000000000000"
emitter = SqlAlchemyKernelEmitter(tenant_id=tenant_id)
payload = {"model": "gpt-4o", "prompt_tokens": 100}
key = IdempotencyKey(
tenant_id=tenant_id,
trace_id="trace-X",
span_id="span-1",
event_type="llm_call",
content_hash=compute_content_hash(payload),
)
try:
emitter.record_llm_call(EmitLlmCallArgs(
span_id="span-1",
model="gpt-4o",
prompt_tokens=100,
completion_tokens=50,
pricing_version="litellm-live",
cost_eur=0.0042,
cost_status="calculated",
idempotency_key=key,
))
# 200 — inserted, OR 200 — silent replay (same key + same content)
except IdempotencyConflict:
# 409 — same logical identity, different content
raise
The emitter:
- Opens a
tenant_scoped_session(tenant_id)so RLS engages on INSERT (migration 0009 — fail-closed for an unset GUC). - Stamps the
idempotency_keyUNIQUE; on collision, dispatches by constraint name (<table>_idempotency_key_key→ 200 silent replay,uq_<table>_logical→ 409 raise). - For LLM and connector calls, upserts a
pricing_snapshotsrow keyed on(tenant_id, model, snapshot_date)(migration 0011) and stores the FK on the call row.pricing_version="unavailable"skips the snapshot — thecost_statuscolumn carries the authoritative signal.
Backend wiring (deferred to US-CR.0)
PlmTenantMiddleware is published in plm_shared.middleware but is
NOT yet installed in 02_App/backend/main.py. US-CR.0 owns the
ordering — it adds IdentityMiddleware first, then plumbs
PlmTenantMiddleware behind it, then wires CORS so that
CORSMiddleware wraps both (failure responses still carry CORS
headers). Conv D leaves the middleware available-but-unwired so
US-CR.0 lands the full ordering in one place.
Migration chain
| Rev | Subject |
|---|---|
| 0001 | foundational tables tenants, runs + quality_level ENUM + actor_kind ENUM |
| 0002 | telemetry_events + composite index + UNIQUE idempotency_key |
| 0003 | llm_calls + cost_* columns + UNIQUE idempotency_key |
| 0004 | connector_calls + connector-specific fields |
| 0005 | artifacts + storage_uri + retention_until |
| 0006 | quality_level + cost_status transition triggers |
| 0007 | mcrc_v1_view (§1-§8 projection) |
| 0008 | Postgres roles plm_kernel_writer + plm_migrator |
| 0009 | Row-Level Security on the 5 tenant-scoped tables + plm_migrator BYPASSRLS |
| 0010 | logical UNIQUE constraints (backs the 200/409 IdempotencyKey contract) |
| 0011 | pricing_snapshots dedup table + nullable FKs |
| 0012 | plm_migrator table privileges (SELECT/INSERT/UPDATE/DELETE on the 7 tenant-scoped tables — closes the privilege gap surfaced by the live-Postgres validation event; BYPASSRLS alone does not grant table access) |
Run the chain with alembic upgrade head from 02_App/plm-shared/
(needs PG_DSN exported).
Install (developer)
From the 02_App/backend/ virtualenv:
cd 02_App/backend
pip install -e ../plm-shared # base install
pip install -e "../plm-shared[db]" # + SQLAlchemy / psycopg / alembic
pip install -e "../plm-shared[s3]" # + boto3 (ObjectStoreBackend)
pip install -e "../plm-shared[db,s3]" # combined
The 02_App/backend/requirements.txt already lists -e ../plm-shared
so a clean pip install -r requirements.txt from 02_App/backend/
picks it up. pip install MUST run from 02_App/backend/ because
pip resolves the editable path relative to the invocation CWD.
Tests
cd 02_App/plm-shared
python -m pytest tests/ -v
Audit + migration suites under tests/audit/ and tests/migrations/
are gated on PG_DSN. Bring up the dev compose first:
docker compose -f dev/postgres.yml up -d
export PG_DSN=postgresql+psycopg://tracepulse:tracepulse@localhost:5432/tracepulse_dev
cd 02_App/plm-shared
python -m pytest tests/audit/ tests/migrations/ -v
S3-protocol live tests under tests/test_object_store_backend.py gate
on S3_ENDPOINT_URL (skip-when-unset). Bring up minio (or any
S3-protocol endpoint) and export S3_ENDPOINT_URL, S3_ACCESS_KEY,
S3_SECRET_KEY, S3_BUCKET.
Architecture conformance gate (US-W1.0 / AC-4)
pyproject.toml declares one [tool.importlinter] Forbidden contract
banning sqlalchemy from every plm-shared module except db and
sqlalchemy_kernel_emitter. Run it from the package root:
cd 02_App/plm-shared
lint-imports --config pyproject.toml
The companion BL5 rule in
02_App/backend/scripts/architecture_guardrails.py (warn-only at
launch) regex-scans for INSERT INTO {telemetry_events|llm_calls|connector_calls|pricing_snapshots}
outside kernel_api / sqlalchemy_kernel_emitter / migrations.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters