Skip to main content

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/ into github.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_key UNIQUE; 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_snapshots row keyed on (tenant_id, model, snapshot_date) (migration 0011) and stores the FK on the call row. pricing_version="unavailable" skips the snapshot — the cost_status column 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

plm_shared-1.0.2.tar.gz (214.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

plm_shared-1.0.2-py3-none-any.whl (197.2 kB view details)

Uploaded Python 3

File details

Details for the file plm_shared-1.0.2.tar.gz.

File metadata

  • Download URL: plm_shared-1.0.2.tar.gz
  • Upload date:
  • Size: 214.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for plm_shared-1.0.2.tar.gz
Algorithm Hash digest
SHA256 c23b8a3b3cc9bbc43469af8a1651bcc5d00b935708fbe41f5f1b49e6c6855574
MD5 c758c902b2401731e6a6056fc59eb5a5
BLAKE2b-256 6db55cf87275eced91a9bce90c85596ad785865789a56f8c135fa66f6707ea5c

See more details on using hashes here.

File details

Details for the file plm_shared-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: plm_shared-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 197.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for plm_shared-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d9f5cfcdcf28d7568175293514ed142eecaa33f545732c887d4fe393b731545e
MD5 c26d62f45268d044bf0246dcef7a69a3
BLAKE2b-256 662411bb086d601b6f91eebdf0702bcb31755aad48a6adcc46e796906c2777a4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page