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

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 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

plm_shared-1.0.1.tar.gz (215.8 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.1-py3-none-any.whl (198.2 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for plm_shared-1.0.1.tar.gz
Algorithm Hash digest
SHA256 bb8b261c4f891a25b1cdcc8f1fe2fc26644f210553fffa81928a830fcde3999d
MD5 ad55b96a36127b582a1cb15c81f0547d
BLAKE2b-256 c966a4e7d80a7c5f0e176c306f48e4b136d188ddec775102aa6ea2a948e69fc9

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for plm_shared-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1f9209d7f9ff24f375354609541162fe4faca11a65bba640b070687a09908ba5
MD5 b08d9762c642712a4dc6e9064abb0cd7
BLAKE2b-256 a13fa24ba304b052a5a7dae8a5b53bf4f37557175d99801e634aa3aa20d90d8b

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