diff --git a/.claude/agents/linux-media-expert.md b/.claude/agents/linux-media-expert.md index 816843115..077a8d138 100644 --- a/.claude/agents/linux-media-expert.md +++ b/.claude/agents/linux-media-expert.md @@ -24,7 +24,7 @@ You are the Linux media-capture and environment specialist. You own the seams wh - **Exactly one consumer per `/dev/videoN`.** V4L2 returns EBUSY at the kernel level if two processes open the same node. The loop serializes rig work; never launch a second capture against a device already in use. - **v4l2loopback needs `exclusive_caps=0`** (caps=1 breaks ffmpeg→loopback writes) and does not tolerate `poll()` before `VIDIOC_STREAMON` — a strict-conformance driver that has exposed real MMAP-path bugs the permissive vivid driver hides. - **A FAILED cross-device DMA-BUF import probe still perturbs NVIDIA's OPAQUE_FD allocation accounting.** A `vkAllocateMemory` chained with an import-FD info is NOT side-effect-free on NVIDIA even when it returns cleanly; per-handle-type kernel accounting carries forward. Gate such probes by vendor where the engine already does. -- **Helper-process Vulkan is import-side only** (FD import + bind + map + layout transitions + timeline wait/signal) — allocation, modifier choice, and kernel construction all live in the parent. The RHI boundary (`.claude/rules/rhi.md`) and the polyglot rule (`.claude/rules/polyglot.md`) apply to any capture code you touch. +- **Helper-process Vulkan is import-side only** (FD import + bind + map + layout transitions + timeline wait/signal) — allocation, modifier choice, and kernel construction all live in the parent. The RHI boundary (`.claude/rules/rhi.md`) applies to any capture code you touch. - **Camera / display processor code goes through `GpuContext`, never raw Vulkan.** DMA-BUF import and modifier handling that crosses into Vulkan belongs behind the RHI. ## What to re-derive from code (never cache here) diff --git a/.claude/agents/polyglot-ipc-expert.md b/.claude/agents/polyglot-ipc-expert.md index 4ed009df6..e52e3a8ad 100644 --- a/.claude/agents/polyglot-ipc-expert.md +++ b/.claude/agents/polyglot-ipc-expert.md @@ -17,7 +17,7 @@ You are the helper-process / IPC specialist. You own the wire between the parent ## Method — how you work - **Python is the sole focus runtime** (`docs/plan/ARCHITECTURE.md` §Language SDKs & parity). TypeScript authoring is paused, not rejected; a future SDK follows the same importable-library model. Do not design a second-runtime abstraction into a surface that has one runtime today — and when the plan entry is OPEN, stop and escalate rather than assuming either way. -- **The escalate-op recipe is: edit the JTD schema → regenerate → rebuild both halves → paired tests.** A schema edit is followed by `cargo xtask generate-schemas` and a rebuild of the Rust parent and the Python wheel so the wire shapes stay identical. The op isn't done until a parent-side Rust test and a helper-side Python test both exercise it. +- **The escalate-op recipe is: edit the Rust wire types → mirror the helper's dict construction → extend the round-trip vectors → paired tests.** There is no codegen and no schema file. The types in `runtime/streamlib-engine/src/core/compiler/compiler_ops/subprocess_escalate_wire_types/` are hand-written serde structs and the helper builds the same documents as plain Python dicts, so serde's encoding *is* the agreement between the two sides — field names, variant spellings and optional-field omission are not free to change. `escalate_wire_encoding_tests.rs` holds the vectors. The op isn't done until a parent-side Rust test and a helper-side Python test both exercise it. - **Serve a new capability as its own escalate op, never as an emulated callback scope.** The parent bridge answers per-op round trips; a design that ships a scope object into the helper and calls back is the shape to reject. - **On a test that hangs with no output, suspect PUBSUB-without-init first.** PUBSUB silently no-ops when uninitialized — subscribe buffers, publish drops — so a subscribe/publish/join test blocks forever with no panic and no error. Initialize it (or run inside a real runtime), use a timed channel receive instead of a bare join, and allow the subscriber setup time before publishing. diff --git a/.claude/rules/polyglot.md b/.claude/rules/polyglot.md deleted file mode 100644 index 800a0203e..000000000 --- a/.claude/rules/polyglot.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -paths: - - "sdk/streamlib-python-wheel/**" - - "runtime/streamlib-ipc-types/**" - - "packages/escalate/**" ---- - -# Polyglot - -- **One Python processor, one helper process, one GIL** — hosting a processor in the app's - interpreter is a STOP-WORK violation. See `.claude/rules/placement.md`. -- **Python is the sole focus runtime** (`docs/plan/ARCHITECTURE.md` §Language SDKs & parity). - TypeScript authoring is paused, not rejected, and a future SDK follows the same - importable-library model — so a surface built for Python today owes nothing to a second - runtime, and "parity" never means shipping two SDKs in one change. -- **A schema change regenerates every consumer of that schema.** An `escalate_*.yaml` (or any - JTD schema) edit is followed by `cargo xtask generate-schemas` and a rebuild of the Rust - parent and the Python wheel together, so the two halves of the escalate wire stay in - lock-step. -- **Helper-process Vulkan is the import-side carve-out only** — `vkImportMemoryFdKHR` + bind + - map, layout transitions on imported handles, timeline wait/signal. No allocation, no modifier - choice, no kernel construction; everything privileged escalates to the parent. diff --git a/.claude/scripts/ship-change-removed-gate.sh b/.claude/scripts/ship-change-removed-gate.sh index 11477f33d..ca097ce38 100755 --- a/.claude/scripts/ship-change-removed-gate.sh +++ b/.claude/scripts/ship-change-removed-gate.sh @@ -40,7 +40,7 @@ trap 'rm -f "$hits"' EXIT # breaks a deadlock: /ship-change gates at step 1 but folds # ARCHITECTURE.md at step 3, so a change whose own plan text names # what it removes could never reach the step that retires that text. -# examples/** consumers, lagging by design (CLAUDE.md); moving out-of-repo (#1672). +# examples/** consumers, lagging by design (CLAUDE.md). # packages/ the downstream-consumer entries only, same doctrine — see below. # vendor/** the vendored vulkanalia fork, never ours to edit. # The path check inherits none of these: a file existing at a named path is residue diff --git a/.claude/scripts/tests/ship-change-removed-gate.test.sh b/.claude/scripts/tests/ship-change-removed-gate.test.sh index 80f6aea76..17c1eb008 100755 --- a/.claude/scripts/tests/ship-change-removed-gate.test.sh +++ b/.claude/scripts/tests/ship-change-removed-gate.test.sh @@ -187,7 +187,7 @@ plant docs/learnings/slpkg-raw-device-rhi-construction.md "A separately-built .f run_gate expect_pass "a learning that records driver behaviour is not residue" -# Consumers lag by design (CLAUDE.md) and are moving out-of-repo (#1672). +# Consumers lag by design (CLAUDE.md). new_repo <<'EOF' - REMOVED: fictional_modules EOF diff --git a/CLAUDE.md b/CLAUDE.md index 1de7f12e4..3fa040d2c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -96,10 +96,12 @@ before implementation. Sessions implement the plan; they never make architecture for this session. Do not file tickets for it. - File an issue only when something blocks the current milestone. Non-blocking findings go in the PR description as a note, then we move on. Getting an MVP into users' hands beats completeness. -- These directories are moving to `tatolab/streamlib-packages` (#1672). Reading them is allowed — - they are reference material for parity and completeness checks (how processors are actually - written, which API surfaces real code exercises). Editing stays deny-ruled: they are never bent - to make an engine change pass, and never treated as contract sources. +- They are the pre-pivot tree, **not a model for new code**. Each is written against the + deleted identity grammar, the deleted schema layer, `streamlib.yaml` manifests and the + package-as-distributable shape the wheel replaced — so reading one to learn how a processor + is declared teaches the model we removed. Their *logic* still holds (how a codec was wired to + the RHI, what a capture path must handle); read for that, never for form. Editing stays + deny-ruled. Captured knowledge lives in `docs/learnings/`; design rationale in `docs/decisions/`. However, these may go stale and should be verified, not viewed as facts. It serves as a cache. Everything else is re-derived from code at need — do not create summary docs of what code already shows. diff --git a/docs/architecture/adapter-authoring.md b/docs/architecture/adapter-authoring.md index 675c5503d..21f9d29fb 100644 --- a/docs/architecture/adapter-authoring.md +++ b/docs/architecture/adapter-authoring.md @@ -479,30 +479,12 @@ ambient availability) lives in the *Trade-off* section of adapter setup is per-runtime and lifetime-controlled, neither of which a Cargo feature can express. -## Polyglot coverage - -If the adapter is supposed to be reachable from Python and Deno -subprocesses (which is the default for any new adapter), follow -[`.claude/rules/polyglot.md`](../../.claude/rules/polyglot.md): - -- Cdylibs (`streamlib-python-native`, `streamlib-deno-native`) add - the adapter crate as a runtime dep. The cdylib's dep graph - must still exclude `streamlib` — `cargo tree -p - streamlib-python-native | grep -c "^streamlib v"` should return - `0`. CI enforces this via `cargo xtask check-boundaries` (see - CLAUDE.md → Vulkan RHI Boundary). -- The Python adapter mirror at - `sdk/streamlib-python/python/streamlib/adapters/` carries the - per-adapter context types (`VulkanContext`, `OpenGLContext`, etc.); - the base `SurfaceAdapter` Protocol/interface lives in - `sdk/streamlib-python/python/streamlib/surface_adapter.py`. It mirrors - the trait shape using Python's idiomatic scope binding (`with`). - Escalate-op schemas live - in `packages/escalate/schemas/`. -- Polyglot coverage is **both Python AND Deno together** (per - `polyglot.md`). The only legitimate split is schema-only / - language-specific by construction; document the reason in the - PR if you split. +> A "Polyglot coverage" section was removed here: every artifact it named — +> the `streamlib-python-native` / `streamlib-deno-native` cdylibs, the +> `sdk/streamlib-python/` adapter mirror, `packages/escalate/schemas/`, and the +> Deno half of "both Python AND Deno together" — has been deleted. Adapters are +> statically linked into the wheel, and a helper process imports that same wheel +> rather than a separate cdylib. ## Cross-process producer composition @@ -883,7 +865,5 @@ Read these, in this order, when authoring: `VulkanComputeKernel`, the dispatch primitive any adapter that needs compute reaches through (via escalate IPC from subprocess). -- [`.claude/rules/polyglot.md`](../../.claude/rules/polyglot.md) - — polyglot rules including the import-side carve-out. - [`.claude/rules/rhi.md`](../../.claude/rules/rhi.md) — the RHI + import-side carve-out rule adapter work rides. diff --git a/docs/architecture/adapter-runtime-integration.md b/docs/architecture/adapter-runtime-integration.md index aa80da2ae..e667a2f66 100644 --- a/docs/architecture/adapter-runtime-integration.md +++ b/docs/architecture/adapter-runtime-integration.md @@ -178,7 +178,7 @@ the FD is already an artifact; what the subprocess does with it is bounded: 1. `VkImportMemoryFdInfoKHR` (the import-side carve-out from - `.claude/rules/polyglot.md`) + `.claude/rules/rhi.md`) 2. `vkBindImageMemory` / `vkBindBufferMemory` 3. Layout transitions + sync wait/signal on imported handles 4. Render or compute against the imported handle @@ -484,8 +484,8 @@ property is load-bearing. subprocess obtains a usable RHI surface beyond the import-side carve-out (the integration-shape view of how the carve-out works alongside this doc's adapter-runtime-shape view) -- `.claude/rules/polyglot.md` — the polyglot rule, including - the import-side carve-out +- `.claude/rules/rhi.md` — the RHI boundary, including the + import-side carve-out - `docs/architecture/adapter-authoring.md` — the adapter implementation contract - `docs/learnings/nvidia-egl-dmabuf-render-target.md` — diff --git a/docs/architecture/subprocess-rhi-parity.md b/docs/architecture/subprocess-rhi-parity.md index bd82d0e66..549e78627 100644 --- a/docs/architecture/subprocess-rhi-parity.md +++ b/docs/architecture/subprocess-rhi-parity.md @@ -266,8 +266,6 @@ Revisit when: `VulkanGraphicsKernel`. - [ray-tracing-kernel.md](ray-tracing-kernel.md) — host's `VulkanRayTracingKernel`. -- [`.claude/rules/polyglot.md`](../../.claude/rules/polyglot.md) - — the polyglot rule the carve-out lives under. - [`.claude/rules/rhi.md`](../../.claude/rules/rhi.md) — the RHI + import-side carve-out rule. - [`docs/learnings/`](../learnings/) — bug evidence motivating one host diff --git a/docs/plan/OPERATING-MODEL.md b/docs/plan/OPERATING-MODEL.md index d4f4e29d5..f2a45ffb0 100644 --- a/docs/plan/OPERATING-MODEL.md +++ b/docs/plan/OPERATING-MODEL.md @@ -293,15 +293,14 @@ PR — never accreted mid-session because something annoyed an agent once. 1. **Commit the pivot** — today's uncommitted rule/CLAUDE.md/settings edits + this document, one operating-model PR. (`docs/plan/` is currently untracked; nothing enforces a plan that isn't in git.) -2. **Land #1672** — consumers out; kills the build coupling. -3. **Wave 1 skills** — `grilling`, `batch-grilling`, `glossary`, `/align`, `/plan`: +2. **Wave 1 skills** — `grilling`, `batch-grilling`, `glossary`, `/align`, `/plan`: just enough to run the plan session. -4. **The plan session** — `/align` on §Product first (the MVP sentence), then module +3. **The plan session** — `/align` on §Product first (the MVP sentence), then module system, processor model, SDK parity, in that order. Everything after this is execution. -5. **Wave 2 skills** — `/reconcile-tracker` first (clean today's misaligned milestones +4. **Wave 2 skills** — `/reconcile-tracker` first (clean today's misaligned milestones and tickets against the fresh plan), then `/propose-change`, `/derive-tickets`, `/implement`, `review-pr` consolidation: the first real change flows through them. -6. **Wave 3** — `/ship-change`, `/pivot`, `/hygiene` (whose first full run seeds the +5. **Wave 3** — `/ship-change`, `/pivot`, `/hygiene` (whose first full run seeds the kill-list), then the docs-consolidation change (retiring `docs/architecture/`) as the first big change run through the new system — the system migrates the old docs, proving itself on its own bootstrap.