AGENT SKILL

bubbles-isolated-ml-sidecar

Enforce the isolated-ML-sidecar invariant — the Python/ML tier is COMPUTE ONLY and reaches data ONLY through the owning strongly-typed service tier over a typed contract wire (service-gated HTTP+protobuf OR bus-gated messaging), never holding database/cache/queue credentials. Use when adding or reviewing a Python/ML/LLM/embedding/data-science sidecar; when a Python service imports a data-store driver (psycopg/asyncpg/sqlalchemy/redis/kafka) or reads DATABASE_URL/REDIS_URL/RABBITMQ_URL; when wiring a python-compute-only-guard into pre-push/CI; when reconciling "removed the web framework" shorthand against a retained protobuf transport; or when deciding whether a product is conformant-by-architecture (Go-only/Rust-only, no Python data plane).

License 许可
MIT
最近更新
2026-11-07
Stars
★ 1
Forks
0

01 前言 Front Matter

name
bubbles-isolated-ml-sidecar
description
Enforce the isolated-ML-sidecar invariant — the Python/ML tier is COMPUTE ONLY and reaches data ONLY through the owning strongly-typed service tier over a typed contract wire (service-gated HTTP+protobuf OR bus-gated messaging), never holding database/cache/queue credentials. Use when adding or reviewing a Python/ML/LLM/embedding/data-science sidecar; when a Python service imports a data-store driver (psycopg/asyncpg/sqlalchemy/redis/kafka) or reads DATABASE_URL/REDIS_URL/RABBITMQ_URL; when wiring a python-compute-only-guard into pre-push/CI; when reconciling "removed the web framework" shorthand against a retained protobuf transport; or when deciding whether a product is conformant-by-architecture (Go-only/Rust-only, no Python data plane).

03 SKILL.md 正文

Isolated ML Sidecar (Compute-Only Invariant)

Portability

Portable governance skill. The INVARIANT and the guard shape are architecture-level and repo-neutral. Named realizations (smackerel, the WA/QF sibling specs) are illustrative anchors, not part of the rule — a fresh product realizes the same invariant with its own service tier and its own guard wired through its own runner. Keep this skill free of hostnames, ports, and repo-only commands; resolve command execution through .specify/memory/agents.md per agent-common.md.

The Invariant (NON-NEGOTIABLE)

The Python / ML sidecar is COMPUTE ONLY. It ingests a typed request, computes (inference, embedding, scoring, feature extraction, LLM calls), and returns a typed response. It MUST NOT:

  • hold database / cache / queue credentials,
  • import a data-store driver,
  • read a data-store connection URL,
  • reach a database, cache, or message store directly.

It reaches persistent data ONLY via the owning strongly-typed service tier (Rust or Go), over a typed contract wire. The service tier owns the credentials, the schema, the migrations, and the connection pool; the sidecar owns none of them.

The single sentence: the sidecar computes, the typed service tier persists.

Use This Skill When

  • Adding or reviewing a Python / ML / LLM / embedding / data-science service.
  • A Python module imports psycopg, psycopg2, asyncpg, sqlalchemy, redis, aioredis, kafka, confluent_kafka, pymongo, or uses a message-bus client as a storage layer.
  • A Python service reads DATABASE_URL, POSTGRES_URL, REDIS_URL, RABBITMQ_URL, or an equivalent infra connection string.
  • Wiring a python-compute-only-guard-style check into pre-push / CI.
  • Reconciling operator shorthand like "removed the web framework" against a transport that is legitimately retained (see Transport-Posture Reconciliation).
  • Deciding whether a product needs the guard at all (see When It Does NOT Apply).

Two Sanctioned Transport Realizations (both valid — do not mandate one)

The invariant is transport-agnostic. Two realizations are sanctioned; pick the one that matches the product's architecture. Do NOT force a bus onto a request/response product or an HTTP call onto an event-driven one.

1. Service-gated 2. Bus-gated
Shape Python calls the typed service tier synchronously Message in → compute → message out
Wire HTTP with a protobuf-only body (JSON forbidden for business data) Message-bus subject/topic carrying a typed payload
Transport role A web framework (e.g. FastAPI) is retained purely as a protobuf ASGI transport with a proto route registry — not as a JSON API and not as a data layer A bus client (e.g. NATS) drives the sidecar; no DB driver present
Data access Only via typed calls back into the service tier Only via messages to/from the service tier
Removed database_url / asyncpg / redis / sqlalchemy are REMOVED any storage driver is absent by construction
Illustrative anchor the Rust-services sibling refactored under WA spec 161 (Scope C) smackerel's NATS-driven ML sidecar

Both realizations satisfy the invariant identically: the sidecar never touches a store; it only exchanges typed messages with the tier that does.

Transport-Posture Reconciliation

Operator shorthand such as "we removed FastAPI" or "removed the web framework" almost always means "removed the storage + JSON API surface", NOT "removed the transport." In the service-gated realization the web framework is legitimately retained as a protobuf ASGI transport. Before acting on a removal instruction, reconcile the shorthand against the two axes:

  • Storage + JSON business API → REMOVE (this is the compute-only invariant).
  • Protobuf transport + proto route registry → KEEP (this is how the sidecar talks to the typed tier).

Record the resolved posture explicitly in the spec/design so a later reader does not "finish the removal" and break the transport.

Enforcement: python-compute-only-guard

Each downstream product wires a compute-only guard into its existing pre-push / CI gate (invoked through the repo runner, never a bare ad-hoc command). The guard is a static scan of the Python surface with no bypass flag (--skip / --force / --ignore / --allow-once MUST NOT exist). Three checks:

  1. Forbidden-driver scan — fail if the Python surface imports a data-store driver: psycopg, psycopg2, asyncpg, sqlalchemy, redis / aioredis, kafka / confluent_kafka, pymongo, or a bus client used as storage.
  2. Direct-infra-URL-read scan — fail if the Python surface reads DATABASE_URL, POSTGRES_URL, REDIS_URL, RABBITMQ_URL, or an equivalent infra connection string.
  3. REMOVED-marker persistence — assert the sanctioned REMOVED/absence markers (e.g. the config keys that once held database_url) are still gone, so a future edit cannot silently re-add a data path.

An allowlist scopes the scan to the compute-only surface (excluding, e.g., a legitimately data-owning migration tool that is NOT the sidecar). The guard is wired into the product's own pre-push flow and CI — mirror the canonical implementations: WA spec 161 (Scope C) and its cross-product sibling QF spec 089 (Scope C).

Forbidden vs Required

❌ Forbidden (in the Python/ML sidecar) ✅ Required
import asyncpg / import psycopg2 / from sqlalchemy import ... No data-store driver; call the typed service tier
redis.Redis(...) / aioredis.from_url(...) Cache is owned by the service tier; request via typed call/message
os.environ["DATABASE_URL"] / reading REDIS_URL / RABBITMQ_URL Sidecar env carries NO infra connection strings
A JSON business API (response_model=...JSON...) for cross-tier data Protobuf-only wire (service-gated) or typed bus payload (bus-gated)
"Removing FastAPI" and deleting the protobuf transport too Keep the protobuf ASGI transport; remove only storage + JSON API
A generator emitting the 4 infra URLs into the sidecar's .env Per-service env filter: omit infra URLs for compute-only Python
A compute-only guard with a --skip/--force bypass No bypass; a new legitimate surface is added to the allowlist, reviewed

Rationale (security framing)

The least-trusted, fastest-churning dependencies in the whole system live in the sidecar: LLM clients, embedding libraries, model SDKs, reverse-engineered/third-party connectors, and the long tail of the Python data-science stack. Keeping the sidecar credential-free bounds the blast radius of a compromised, malicious, or merely flaky ML dependency — a popped pip package in the sidecar cannot reach the database because there is no driver and no URL to reach it with. The service tier (statically typed, slower-churning, smaller trusted dependency set) remains the only thing holding data-store credentials.

This is also the primary structural cure for "Python development flakiness": the sidecar cannot corrupt state, cannot leak a connection pool, and cannot run a rogue migration, because it has no path to persistent state at all. Data-plane correctness is confined to the typed tier.

This composes with — and does not replace — bubbles-supply-chain-source-locking: source-locking bounds where the sidecar's dependencies come from; this skill bounds what a compromised one can reach.

When It Does NOT Apply (N/A path — conformant-by-architecture)

A product with no Python data plane is conformant by construction and needs no guard:

  • A Go-only or Rust-only product whose services own their own data access in the typed tier, with no Python/ML sidecar, has nothing to isolate — the invariant is satisfied structurally. Document this as an explicit N/A in the product's spec/instructions rather than wiring an empty guard.
  • A product whose Python exists ONLY as a build-time tool (codegen, linting, migration authoring) — not a runtime data-plane participant — is out of scope; the guard's allowlist excludes it.

Record the N/A path so an auditor sees a deliberate "conformant-by-architecture" decision, not an oversight.

Audit Checklist (agent-runnable against any product's Python surface)

Run through the repo runner / standard search tools; capture real output (≥10 lines) per agent-common.md evidence standard. Do not fabricate results.

  1. Driver scan — search the Python surface for psycopg|asyncpg|sqlalchemy|redis|aioredis|kafka|confluent_kafka|pymongo. Expectation: zero imports in the sidecar.
  2. Infra-URL scan — search for reads of DATABASE_URL|POSTGRES_URL|REDIS_URL|RABBITMQ_URL. Expectation: none resolved by the sidecar (a generator may synthesize them for the service tier — confirm they are NOT delivered to the Python container's env).
  3. Env-delivery check — inspect the live sidecar container's environment (compose environment: allowlist or filtered per-service .env). Expectation: no infra connection strings present.
  4. Transport-posture check — if a web framework is present, confirm it is a protobuf transport with a proto route registry, NOT a JSON business API. Confirm JSON is not used for cross-tier business data.
  5. REMOVED-marker check — confirm the sanctioned absence markers (former database_url keys, etc.) are still gone.
  6. Guard-wiring check — confirm a compute-only guard runs in the product's pre-push AND CI, and that it has NO bypass flag.
  7. N/A decision check — if there is no Python data plane, confirm the spec/instructions record the conformant-by-architecture rationale.

When NOT to Use

Works Well With

References

04 同仓库更多 Skills

pkirsanov
pkirsanov/smackerel

smackerel-trace-capture

Wire Smackerel's concrete validate-plane stack into the framework trace-driven defect-discovery method. Use when running live-category tests (integration/e2e/e2e-ui/stress) against the ephemeral test stack; capturing SLO or trace evidence; hunting latency/error/perf bugs from spans; querying the test-stack Jaeger or Prometheus; or touching `.specify/runtime/observability`. Concrete smackerel endpoints + the exact `capture-slo.sh` command; do NOT duplicate `bubbles-observability-adapter` — this only adds the wiring.

测试与质量
★ 1
pkirsanov
pkirsanov/smackerel

smackerel-no-defaults

Enforce Smackerel's NO-DEFAULTS / fail-loud SST policy. Use when editing config, Docker Compose, deploy adapters, docs, instructions, skills, env handling, host bind addresses, or any runtime value that could be hidden by fallback syntax.

运维与部署
★ 1
pkirsanov
pkirsanov/smackerel

bubbles-workflow-mode-resolution

Resolve a Bubbles workflow mode against `bubbles/workflows.yaml`, including template inheritance, status ceilings, gate wiring, and decision policy. Use when picking a mode for a request, when a guard reports a mode-related failure, or when extending workflows.yaml with a new mode or template. Covers natural-language intent routing and the workflow registry consistency rules.

Skill与智能体开发
★ 1
pkirsanov
pkirsanov/smackerel

bubbles-workflow-execution-loops

Run per-round synchronous workflow execution loops correctly. Use when orchestrating a multi-round workflow, dispatching specialist subagents, handling round-based remediation, or interpreting a workflow mode's execution policy. Covers authorized direct execution, dispatch-and-wait, batch-then-summarize prohibition, and mapped-mode execution.

Skill与智能体开发
★ 1
pkirsanov
pkirsanov/smackerel

bubbles-vscode-agent-constraints

Design agents that fit the VS Code Copilot Chat runtime instead of fighting it. Use when authoring or editing any `*.agent.md`; when adding `tools:`, `agents:`, `handoffs:`, `model:`, `user-invocable:`, or `disable-model-invocation:` frontmatter; when designing an orchestrator that dispatches specialists; when a design implies agent-calls-agent-calls-agent chaining; when a subagent dispatch silently no-ops or an agent cannot be selected in the dropdown; or when writing a governance rule about delegation that the runtime cannot actually enforce. Prevents multi-level-subagent designs, prose-only dispatch laws, and frontmatter/body contradictions.

视觉设计Skill与智能体开发
★ 1
pkirsanov
pkirsanov/smackerel

bubbles-upkeep-cadence

How to schedule, dispatch, and record recurring operational upkeep tasks (backup, restore-drill, BCDR-drill, patch-cycle, secret-rotation, flag-cleanup, compliance-sweep). Use when authoring or modifying `config/upkeep-calendar.yaml`, when investigating a missed task, when reading the upkeep ledger, or when adding a new task type.

★ 1