[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6N95GXDPCGBCXMQHCQPHDBV":3,"skill-same:01M6N95GXDPCGBCXMQHCQPHDBV":64},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":46,"repositoryHtmlUrl":47,"repositoryHomepageUrl":6,"ownerHtmlUrl":48,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":49,"install":50,"resources":63},"01M6N95GXDPCGBCXMQHCQPHDBV","bubbles-isolated-ml-sidecar",null,"Enforce the isolated-ML-sidecar invariant — the Python\u002FML 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\u002Fcache\u002Fqueue credentials. Use when adding or reviewing a Python\u002FML\u002FLLM\u002Fembedding\u002Fdata-science sidecar; when a Python service imports a data-store driver (psycopg\u002Fasyncpg\u002Fsqlalchemy\u002Fredis\u002Fkafka) or reads DATABASE_URL\u002FREDIS_URL\u002FRABBITMQ_URL; when wiring a python-compute-only-guard into pre-push\u002FCI; when reconciling \"removed the web framework\" shorthand against a retained protobuf transport; or when deciding whether a product is conformant-by-architecture (Go-only\u002FRust-only, no Python data plane).","pkirsanov\u002Fsmackerel","pkirsanov","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F138962?v=4","01M6N8NAPD2KXMVC17X6GJ0WZ5","01M6N8NATQ9SAZ00H9D61R0TPV",1,"MIT","https:\u002F\u002Fgithub.com\u002Fpkirsanov\u002Fsmackerel\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fbubbles-isolated-ml-sidecar\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"bubbles isolated ml sidecar","ml","never","wiring","imports","plane","URL","sqlalchemy","embedding","data-science","only","go","credentials","data","ci","messaging","database","product","architecture","rust","2026-11-10 10:12:30",false,"---\nname: bubbles-isolated-ml-sidecar\ndescription: Enforce the isolated-ML-sidecar invariant — the Python\u002FML 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\u002Fcache\u002Fqueue credentials. Use when adding or reviewing a Python\u002FML\u002FLLM\u002Fembedding\u002Fdata-science sidecar; when a Python service imports a data-store driver (psycopg\u002Fasyncpg\u002Fsqlalchemy\u002Fredis\u002Fkafka) or reads DATABASE_URL\u002FREDIS_URL\u002FRABBITMQ_URL; when wiring a python-compute-only-guard into pre-push\u002FCI; when reconciling \"removed the web framework\" shorthand against a retained protobuf transport; or when deciding whether a product is conformant-by-architecture (Go-only\u002FRust-only, no Python data plane).\n---\n\n# Isolated ML Sidecar (Compute-Only Invariant)\n\n## Portability\n\nPortable governance skill. The INVARIANT and the guard shape are architecture-level and repo-neutral. Named realizations (`smackerel`, the WA\u002FQF 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\u002Fmemory\u002Fagents.md` per [agent-common.md](..\u002F..\u002Fagents\u002Fbubbles_shared\u002Fagent-common.md).\n\n## The Invariant (NON-NEGOTIABLE)\n\n**The Python \u002F 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:\n\n- hold database \u002F cache \u002F queue **credentials**,\n- import a data-store **driver**,\n- read a data-store **connection URL**,\n- reach a database, cache, or message store **directly**.\n\nIt 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.\n\nThe single sentence: **the sidecar computes, the typed service tier persists.**\n\n## Use This Skill When\n\n- Adding or reviewing a Python \u002F ML \u002F LLM \u002F embedding \u002F data-science service.\n- A Python module imports `psycopg`, `psycopg2`, `asyncpg`, `sqlalchemy`, `redis`, `aioredis`, `kafka`, `confluent_kafka`, `pymongo`, or uses a message-bus client as a storage layer.\n- A Python service reads `DATABASE_URL`, `POSTGRES_URL`, `REDIS_URL`, `RABBITMQ_URL`, or an equivalent infra connection string.\n- Wiring a `python-compute-only-guard`-style check into pre-push \u002F CI.\n- Reconciling operator shorthand like \"removed the web framework\" against a transport that is legitimately **retained** (see Transport-Posture Reconciliation).\n- Deciding whether a product needs the guard at all (see When It Does NOT Apply).\n\n## Two Sanctioned Transport Realizations (both valid — do not mandate one)\n\nThe 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\u002Fresponse product or an HTTP call onto an event-driven one.\n\n| | 1. Service-gated | 2. Bus-gated |\n|---|---|---|\n| **Shape** | Python calls the typed service tier synchronously | Message in → compute → message out |\n| **Wire** | HTTP with a **protobuf-only** body (JSON forbidden for business data) | Message-bus subject\u002Ftopic carrying a typed payload |\n| **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** |\n| **Data access** | Only via typed calls back into the service tier | Only via messages to\u002Ffrom the service tier |\n| **Removed** | `database_url` \u002F `asyncpg` \u002F `redis` \u002F `sqlalchemy` are REMOVED | any storage driver is absent by construction |\n| **Illustrative anchor** | the Rust-services sibling refactored under WA spec 161 (Scope C) | `smackerel`'s NATS-driven ML sidecar |\n\nBoth realizations satisfy the invariant identically: the sidecar never touches a store; it only exchanges typed messages with the tier that does.\n\n## Transport-Posture Reconciliation\n\nOperator 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:\n\n- **Storage + JSON business API → REMOVE** (this is the compute-only invariant).\n- **Protobuf transport + proto route registry → KEEP** (this is how the sidecar talks to the typed tier).\n\nRecord the resolved posture explicitly in the spec\u002Fdesign so a later reader does not \"finish the removal\" and break the transport.\n\n## Enforcement: `python-compute-only-guard`\n\nEach downstream product wires a compute-only guard into its **existing** pre-push \u002F 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` \u002F `--force` \u002F `--ignore` \u002F `--allow-once` MUST NOT exist). Three checks:\n\n1. **Forbidden-driver scan** — fail if the Python surface imports a data-store driver: `psycopg`, `psycopg2`, `asyncpg`, `sqlalchemy`, `redis` \u002F `aioredis`, `kafka` \u002F `confluent_kafka`, `pymongo`, or a bus client used as storage.\n2. **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.\n3. **REMOVED-marker persistence** — assert the sanctioned `REMOVED`\u002Fabsence 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.\n\nAn `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)**.\n\n## Forbidden vs Required\n\n| ❌ Forbidden (in the Python\u002FML sidecar) | ✅ Required |\n|---|---|\n| `import asyncpg` \u002F `import psycopg2` \u002F `from sqlalchemy import ...` | No data-store driver; call the typed service tier |\n| `redis.Redis(...)` \u002F `aioredis.from_url(...)` | Cache is owned by the service tier; request via typed call\u002Fmessage |\n| `os.environ[\"DATABASE_URL\"]` \u002F reading `REDIS_URL` \u002F `RABBITMQ_URL` | Sidecar env carries NO infra connection strings |\n| A JSON business API (`response_model=...JSON...`) for cross-tier data | Protobuf-only wire (service-gated) or typed bus payload (bus-gated) |\n| \"Removing FastAPI\" and deleting the protobuf transport too | Keep the protobuf ASGI transport; remove only storage + JSON API |\n| A generator emitting the 4 infra URLs into the sidecar's `.env` | Per-service env filter: omit infra URLs for compute-only Python |\n| A compute-only guard with a `--skip`\u002F`--force` bypass | No bypass; a new legitimate surface is added to the allowlist, reviewed |\n\n## Rationale (security framing)\n\nThe least-trusted, fastest-churning dependencies in the whole system live in the sidecar: LLM clients, embedding libraries, model SDKs, reverse-engineered\u002Fthird-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.\n\nThis 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.\n\nThis composes with — and does not replace — [bubbles-supply-chain-source-locking](..\u002Fbubbles-supply-chain-source-locking\u002FSKILL.md): source-locking bounds *where* the sidecar's dependencies come from; this skill bounds *what a compromised one can reach*.\n\n## When It Does NOT Apply (N\u002FA path — conformant-by-architecture)\n\nA product with **no Python data plane** is conformant by construction and needs no guard:\n\n- A **Go-only** or **Rust-only** product whose services own their own data access in the typed tier, with no Python\u002FML sidecar, has nothing to isolate — the invariant is satisfied structurally. Document this as an explicit N\u002FA in the product's spec\u002Finstructions rather than wiring an empty guard.\n- 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.\n\nRecord the N\u002FA path so an auditor sees a deliberate \"conformant-by-architecture\" decision, not an oversight.\n\n## Audit Checklist (agent-runnable against any product's Python surface)\n\nRun through the repo runner \u002F standard search tools; capture real output (≥10 lines) per [agent-common.md](..\u002F..\u002Fagents\u002Fbubbles_shared\u002Fagent-common.md) evidence standard. Do not fabricate results.\n\n1. **Driver scan** — search the Python surface for `psycopg|asyncpg|sqlalchemy|redis|aioredis|kafka|confluent_kafka|pymongo`. Expectation: zero imports in the sidecar.\n2. **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).\n3. **Env-delivery check** — inspect the live sidecar container's environment (compose `environment:` allowlist or filtered per-service `.env`). Expectation: no infra connection strings present.\n4. **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.\n5. **REMOVED-marker check** — confirm the sanctioned absence markers (former `database_url` keys, etc.) are still gone.\n6. **Guard-wiring check** — confirm a compute-only guard runs in the product's pre-push AND CI, and that it has NO bypass flag.\n7. **N\u002FA decision check** — if there is no Python data plane, confirm the spec\u002Finstructions record the conformant-by-architecture rationale.\n\n## When NOT to Use\n\n- **Test-time backing-store isolation** → use [bubbles-test-environment-isolation](..\u002Fbubbles-test-environment-isolation\u002FSKILL.md). This skill governs a production runtime tier, not ephemeral test stacks.\n- **Whether two products may share one database in production** → use [bubbles-datastore-isolation](..\u002Fbubbles-datastore-isolation\u002FSKILL.md). That skill governs cross-product topology; this one governs a single product's Python-vs-typed-tier boundary.\n- **Where the sidecar's dependencies are allowed to resolve from** → use [bubbles-supply-chain-source-locking](..\u002Fbubbles-supply-chain-source-locking\u002FSKILL.md).\n- **Config single-source-of-truth for the connection strings themselves** → use [bubbles-config-sst](..\u002Fbubbles-config-sst\u002FSKILL.md).\n\n## Works Well With\n\n- [bubbles-datastore-isolation](..\u002Fbubbles-datastore-isolation\u002FSKILL.md) — the production-topology sibling; together they form the \"isolation doctrine\" (compute-only sidecar + bundled stateful stores).\n- [bubbles-supply-chain-source-locking](..\u002Fbubbles-supply-chain-source-locking\u002FSKILL.md) — bounds where the least-trusted deps come from; complementary blast-radius control.\n- [bubbles-capability-foundation-design](..\u002Fbubbles-capability-foundation-design\u002FSKILL.md) — when the typed service tier is the reusable data-access foundation the sidecar consumes.\n- [bubbles-config-sst](..\u002Fbubbles-config-sst\u002FSKILL.md) — governs the per-service env filter that keeps infra URLs out of the sidecar.\n\n## References\n\n- [agent-common.md](..\u002F..\u002Fagents\u002Fbubbles_shared\u002Fagent-common.md) — anti-fabrication, evidence standard, timeout policy.\n- [bubbles-skills.instructions.md](..\u002F..\u002Finstructions\u002Fbubbles-skills.instructions.md) — skill authoring governance.\n- Canonical guard implementations: WA spec 161 (Scope C), QF spec 089 (Scope C).\n","# Isolated ML Sidecar (Compute-Only Invariant)\n\n## Portability\n\nPortable governance skill. The INVARIANT and the guard shape are architecture-level and repo-neutral. Named realizations (`smackerel`, the WA\u002FQF 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\u002Fmemory\u002Fagents.md` per [agent-common.md](..\u002F..\u002Fagents\u002Fbubbles_shared\u002Fagent-common.md).\n\n## The Invariant (NON-NEGOTIABLE)\n\n**The Python \u002F 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:\n\n- hold database \u002F cache \u002F queue **credentials**,\n- import a data-store **driver**,\n- read a data-store **connection URL**,\n- reach a database, cache, or message store **directly**.\n\nIt 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.\n\nThe single sentence: **the sidecar computes, the typed service tier persists.**\n\n## Use This Skill When\n\n- Adding or reviewing a Python \u002F ML \u002F LLM \u002F embedding \u002F data-science service.\n- A Python module imports `psycopg`, `psycopg2`, `asyncpg`, `sqlalchemy`, `redis`, `aioredis`, `kafka`, `confluent_kafka`, `pymongo`, or uses a message-bus client as a storage layer.\n- A Python service reads `DATABASE_URL`, `POSTGRES_URL`, `REDIS_URL`, `RABBITMQ_URL`, or an equivalent infra connection string.\n- Wiring a `python-compute-only-guard`-style check into pre-push \u002F CI.\n- Reconciling operator shorthand like \"removed the web framework\" against a transport that is legitimately **retained** (see Transport-Posture Reconciliation).\n- Deciding whether a product needs the guard at all (see When It Does NOT Apply).\n\n## Two Sanctioned Transport Realizations (both valid — do not mandate one)\n\nThe 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\u002Fresponse product or an HTTP call onto an event-driven one.\n\n| | 1. Service-gated | 2. Bus-gated |\n|---|---|---|\n| **Shape** | Python calls the typed service tier synchronously | Message in → compute → message out |\n| **Wire** | HTTP with a **protobuf-only** body (JSON forbidden for business data) | Message-bus subject\u002Ftopic carrying a typed payload |\n| **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** |\n| **Data access** | Only via typed calls back into the service tier | Only via messages to\u002Ffrom the service tier |\n| **Removed** | `database_url` \u002F `asyncpg` \u002F `redis` \u002F `sqlalchemy` are REMOVED | any storage driver is absent by construction |\n| **Illustrative anchor** | the Rust-services sibling refactored under WA spec 161 (Scope C) | `smackerel`'s NATS-driven ML sidecar |\n\nBoth realizations satisfy the invariant identically: the sidecar never touches a store; it only exchanges typed messages with the tier that does.\n\n## Transport-Posture Reconciliation\n\nOperator 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:\n\n- **Storage + JSON business API → REMOVE** (this is the compute-only invariant).\n- **Protobuf transport + proto route registry → KEEP** (this is how the sidecar talks to the typed tier).\n\nRecord the resolved posture explicitly in the spec\u002Fdesign so a later reader does not \"finish the removal\" and break the transport.\n\n## Enforcement: `python-compute-only-guard`\n\nEach downstream product wires a compute-only guard into its **existing** pre-push \u002F 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` \u002F `--force` \u002F `--ignore` \u002F `--allow-once` MUST NOT exist). Three checks:\n\n1. **Forbidden-driver scan** — fail if the Python surface imports a data-store driver: `psycopg`, `psycopg2`, `asyncpg`, `sqlalchemy`, `redis` \u002F `aioredis`, `kafka` \u002F `confluent_kafka`, `pymongo`, or a bus client used as storage.\n2. **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.\n3. **REMOVED-marker persistence** — assert the sanctioned `REMOVED`\u002Fabsence 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.\n\nAn `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)**.\n\n## Forbidden vs Required\n\n| ❌ Forbidden (in the Python\u002FML sidecar) | ✅ Required |\n|---|---|\n| `import asyncpg` \u002F `import psycopg2` \u002F `from sqlalchemy import ...` | No data-store driver; call the typed service tier |\n| `redis.Redis(...)` \u002F `aioredis.from_url(...)` | Cache is owned by the service tier; request via typed call\u002Fmessage |\n| `os.environ[\"DATABASE_URL\"]` \u002F reading `REDIS_URL` \u002F `RABBITMQ_URL` | Sidecar env carries NO infra connection strings |\n| A JSON business API (`response_model=...JSON...`) for cross-tier data | Protobuf-only wire (service-gated) or typed bus payload (bus-gated) |\n| \"Removing FastAPI\" and deleting the protobuf transport too | Keep the protobuf ASGI transport; remove only storage + JSON API |\n| A generator emitting the 4 infra URLs into the sidecar's `.env` | Per-service env filter: omit infra URLs for compute-only Python |\n| A compute-only guard with a `--skip`\u002F`--force` bypass | No bypass; a new legitimate surface is added to the allowlist, reviewed |\n\n## Rationale (security framing)\n\nThe least-trusted, fastest-churning dependencies in the whole system live in the sidecar: LLM clients, embedding libraries, model SDKs, reverse-engineered\u002Fthird-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.\n\nThis 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.\n\nThis composes with — and does not replace — [bubbles-supply-chain-source-locking](..\u002Fbubbles-supply-chain-source-locking\u002FSKILL.md): source-locking bounds *where* the sidecar's dependencies come from; this skill bounds *what a compromised one can reach*.\n\n## When It Does NOT Apply (N\u002FA path — conformant-by-architecture)\n\nA product with **no Python data plane** is conformant by construction and needs no guard:\n\n- A **Go-only** or **Rust-only** product whose services own their own data access in the typed tier, with no Python\u002FML sidecar, has nothing to isolate — the invariant is satisfied structurally. Document this as an explicit N\u002FA in the product's spec\u002Finstructions rather than wiring an empty guard.\n- 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.\n\nRecord the N\u002FA path so an auditor sees a deliberate \"conformant-by-architecture\" decision, not an oversight.\n\n## Audit Checklist (agent-runnable against any product's Python surface)\n\nRun through the repo runner \u002F standard search tools; capture real output (≥10 lines) per [agent-common.md](..\u002F..\u002Fagents\u002Fbubbles_shared\u002Fagent-common.md) evidence standard. Do not fabricate results.\n\n1. **Driver scan** — search the Python surface for `psycopg|asyncpg|sqlalchemy|redis|aioredis|kafka|confluent_kafka|pymongo`. Expectation: zero imports in the sidecar.\n2. **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).\n3. **Env-delivery check** — inspect the live sidecar container's environment (compose `environment:` allowlist or filtered per-service `.env`). Expectation: no infra connection strings present.\n4. **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.\n5. **REMOVED-marker check** — confirm the sanctioned absence markers (former `database_url` keys, etc.) are still gone.\n6. **Guard-wiring check** — confirm a compute-only guard runs in the product's pre-push AND CI, and that it has NO bypass flag.\n7. **N\u002FA decision check** — if there is no Python data plane, confirm the spec\u002Finstructions record the conformant-by-architecture rationale.\n\n## When NOT to Use\n\n- **Test-time backing-store isolation** → use [bubbles-test-environment-isolation](..\u002Fbubbles-test-environment-isolation\u002FSKILL.md). This skill governs a production runtime tier, not ephemeral test stacks.\n- **Whether two products may share one database in production** → use [bubbles-datastore-isolation](..\u002Fbubbles-datastore-isolation\u002FSKILL.md). That skill governs cross-product topology; this one governs a single product's Python-vs-typed-tier boundary.\n- **Where the sidecar's dependencies are allowed to resolve from** → use [bubbles-supply-chain-source-locking](..\u002Fbubbles-supply-chain-source-locking\u002FSKILL.md).\n- **Config single-source-of-truth for the connection strings themselves** → use [bubbles-config-sst](..\u002Fbubbles-config-sst\u002FSKILL.md).\n\n## Works Well With\n\n- [bubbles-datastore-isolation](..\u002Fbubbles-datastore-isolation\u002FSKILL.md) — the production-topology sibling; together they form the \"isolation doctrine\" (compute-only sidecar + bundled stateful stores).\n- [bubbles-supply-chain-source-locking](..\u002Fbubbles-supply-chain-source-locking\u002FSKILL.md) — bounds where the least-trusted deps come from; complementary blast-radius control.\n- [bubbles-capability-foundation-design](..\u002Fbubbles-capability-foundation-design\u002FSKILL.md) — when the typed service tier is the reusable data-access foundation the sidecar consumes.\n- [bubbles-config-sst](..\u002Fbubbles-config-sst\u002FSKILL.md) — governs the per-service env filter that keeps infra URLs out of the sidecar.\n\n## References\n\n- [agent-common.md](..\u002F..\u002Fagents\u002Fbubbles_shared\u002Fagent-common.md) — anti-fabrication, evidence standard, timeout policy.\n- [bubbles-skills.instructions.md](..\u002F..\u002Finstructions\u002Fbubbles-skills.instructions.md) — skill authoring governance.\n- Canonical guard implementations: WA spec 161 (Scope C), QF spec 089 (Scope C).\n",{"name":5,"description":7},"main","ed893cc338281a0bb9bb9ecf3415716cb242bb3f",".claude\u002Fskills\u002Fbubbles-isolated-ml-sidecar",0,"https:\u002F\u002Fgithub.com\u002Fpkirsanov\u002Fsmackerel","https:\u002F\u002Fgithub.com\u002Fpkirsanov","2026-11-07 02:54:37",{"source":8,"skillName":5,"commands":51,"prompt":61,"zipAvailable":62},[52,55,58],{"tool":53,"command":54},"npx","npx skills add pkirsanov\u002Fsmackerel --skill bubbles-isolated-ml-sidecar",{"tool":56,"command":57},"bunx","bunx skills add pkirsanov\u002Fsmackerel --skill bubbles-isolated-ml-sidecar",{"tool":59,"command":60},"pnpm","pnpm dlx skills add pkirsanov\u002Fsmackerel --skill bubbles-isolated-ml-sidecar","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fpkirsanov\u002Fsmackerel\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fbubbles-isolated-ml-sidecar\u002FSKILL.md",true,[],[65,96,124,151,172,200],{"skillNo":66,"name":67,"nameZh":6,"description":68,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":69,"categories":70,"keywords":76,"publishedAt":38},"01M6N9V5TBHQT20V5SCCJ1VRBC","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\u002Fe2e\u002Fe2e-ui\u002Fstress) against the ephemeral test stack; capturing SLO or trace evidence; hunting latency\u002Ferror\u002Fperf bugs from spans; querying the test-stack Jaeger or Prometheus; or touching `.specify\u002Fruntime\u002Fobservability`. Concrete smackerel endpoints + the exact `capture-slo.sh` command; do NOT duplicate `bubbles-observability-adapter` — this only adds the wiring.","https:\u002F\u002Fgithub.com\u002Fpkirsanov\u002Fsmackerel\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fsmackerel-trace-capture\u002FSKILL.md",[71],{"categoryNo":72,"categoryName":73,"taxonomyType":74,"taxonomyTypeDesc":75},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[77,78,79,80,81,82,83,84,85,86,87,28,88,89,90,91,92,93,94,95],"trace","capture","smackerel trace capture","perf","observability","prometheus","specify","e2e","discovery","use","slo","evidence","do","latency","runtime","tests","adapter","error","bugs",{"skillNo":97,"name":98,"nameZh":6,"description":99,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":100,"categories":101,"keywords":105,"publishedAt":38},"01M6N9TZDEV26RZ7W34HEFPQE9","smackerel-no-defaults","Enforce Smackerel's NO-DEFAULTS \u002F 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.","https:\u002F\u002Fgithub.com\u002Fpkirsanov\u002Fsmackerel\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fsmackerel-no-defaults\u002FSKILL.md",[102],{"categoryNo":103,"categoryName":104,"taxonomyType":74,"taxonomyTypeDesc":75},"01JBSEEDCAT000000000000011","运维与部署",[106,107,108,109,110,111,112,113,114,86,115,116,91,117,118,119,120,121,122,123],"smackerel no defaults","docker","policy","config","deploy","docker compose","syntax","editing","instructions","compose","enforce","bind","env","host","Adapters","sst","addresses","value",{"skillNo":125,"name":126,"nameZh":6,"description":127,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":128,"categories":129,"keywords":133,"publishedAt":38},"01M6N9TJRS5PF83JH2W4614D4W","bubbles-workflow-mode-resolution","Resolve a Bubbles workflow mode against `bubbles\u002Fworkflows.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.","https:\u002F\u002Fgithub.com\u002Fpkirsanov\u002Fsmackerel\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fbubbles-workflow-mode-resolution\u002FSKILL.md",[130],{"categoryNo":131,"categoryName":132,"taxonomyType":74,"taxonomyTypeDesc":75},"01JBSEEDCAT000000000000023","Skill与智能体开发",[134,135,136,137,108,138,139,140,141,142,143,86,144,145,146,147,148,149,150,21],"workflow","resolution","mode","bubbles workflow mode resolution","workflows","routing","guard","template","decision","Rules","reports","intent","inheritance","with","consistency","natural-language","for",{"skillNo":152,"name":153,"nameZh":6,"description":154,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":158,"publishedAt":38},"01M6N9TC98D6DJR0D64SSN0SN3","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.","https:\u002F\u002Fgithub.com\u002Fpkirsanov\u002Fsmackerel\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fbubbles-workflow-execution-loops\u002FSKILL.md",[157],{"categoryNo":131,"categoryName":132,"taxonomyType":74,"taxonomyTypeDesc":75},[134,159,160,161,162,108,163,164,165,166,167,86,168,169,170,171,136],"execution","loops","bubbles workflow execution loops","summarize","run","batch","remediation","direct","subagents","dispatch","wait","multi","multi-round",{"skillNo":173,"name":174,"nameZh":6,"description":175,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":176,"categories":177,"keywords":182,"publishedAt":38},"01M6N9SYXQNJDQ1T1YT81JH2BG","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\u002Fbody contradictions.","https:\u002F\u002Fgithub.com\u002Fpkirsanov\u002Fsmackerel\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fbubbles-vscode-agent-constraints\u002FSKILL.md",[178,181],{"categoryNo":179,"categoryName":180,"taxonomyType":74,"taxonomyTypeDesc":75},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":131,"categoryName":132,"taxonomyType":74,"taxonomyTypeDesc":75},[183,184,185,186,187,188,189,190,191,192,193,194,113,195,28,91,196,197,198,199],"vscode","constraints","bubbles vscode agent constraints","design","writing","copilot","code","delegation","Tools","subagent","model","chaining","orchestrator","authoring","fit","subagent dispatch","frontmatter",{"skillNo":201,"name":202,"nameZh":6,"description":203,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":204,"categories":205,"keywords":206,"publishedAt":38},"01M6N9SD78SK12PRT9KBRZ6TB3","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\u002Fupkeep-calendar.yaml`, when investigating a missed task, when reading the upkeep ledger, or when adding a new task type.","https:\u002F\u002Fgithub.com\u002Fpkirsanov\u002Fsmackerel\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fbubbles-upkeep-cadence\u002FSKILL.md",[],[207,208,209,210,211,212,213,214,86,215,216,217,218,219,220,221,222,223,224,225],"cadence","bubbles upkeep cadence","tasks","cleanup","backup","sweep","schedule","recurring","patch","restore","task","cycle","record","missed","type","drill","how to","new task","rotation"]