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
Standalone repo since 2026-06-30 (FTR-924). Extracted from
plm-engine-core/shared/intogithub.com/plm-engine/plm-shared. See docs/POST-SPLIT.md for the split context, India onboarding pointer, and PRD §6.1 cross-reference.
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 this repo root (needs
PG_DSN exported).
Install (developer)
In a workspace context (uv workspace at c:\AIxPLM\plm-engine\),
uv sync resolves plm-shared editable as a workspace member. As
an external consumer:
pip install plm-shared # base install — from PyPI
pip install 'plm-shared[db]' # + SQLAlchemy / psycopg / alembic
pip install 'plm-shared[s3]' # + boto3 (ObjectStoreBackend)
pip install 'plm-shared[db,s3]' # combined
Pin per D3 = Option A (additive→minor, breaking→major): plm-shared = "^1.0"
in your sibling's pyproject.toml. See docs/POST-SPLIT.md.
Tests
cd plm-shared
python -m pytest tests/ -v
Audit + migration suites under tests/audit/ and tests/migrations/
are gated on PG_DSN. CI brings up the matching Postgres service in
.github/workflows/audit-pg.yml (postgres:15.7-alpine, tracepulse_ci
DB). Locally, use the workspace dev/postgres.yml:
docker compose -f ../dev/postgres.yml up -d
export PG_DSN=postgresql+psycopg://tracepulse:tracepulse@localhost:5432/tracepulse_dev
cd 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 repo root:
cd 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