{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cell-00",
   "metadata": {},
   "source": [
    "# splime — 0.4.5 cookbook\n",
    "\n",
    "Public notebook marker: splime==0.4.5; updated 2026-07-27.\n",
    "\n",
    "**Reuse Python functions across projects without rewriting or redeploying them.**\n",
    "\n",
    "splime turns trusted Python functions into versioned, portable **nodes** that can be\n",
    "reused across projects and executed locally or remotely.\n",
    "\n",
    "This cookbook is **offline-first**: every cell in Part 1 uses only a local daemon.\n",
    "No account, credential, central-server request, Docker action, or external network\n",
    "access is required. Part 2 contains optional connected examples; every live or\n",
    "mutating control is visibly `False` by default.\n",
    "\n",
    "Before you start:\n",
    "\n",
    "```bash\n",
    "pip install \"splime==0.4.5\"\n",
    "SPL_COOKBOOK_DAEMON_PORT=8765 spl-daemon serve\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-01",
   "metadata": {},
   "source": [
    "## 1. Setup — one import is enough\n",
    "\n",
    "Everything a user needs lives in the top-level `spl` package. A fresh per-kernel\n",
    "namespace is applied to every object, pipeline, library, and recorded run created\n",
    "by this notebook, so it never collides with a generic name in an existing daemon."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-02",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import sys\n",
    "from uuid import uuid4\n",
    "\n",
    "from spl import Deployment, NodeRemote, SPLClient, lift\n",
    "\n",
    "print(\"Python:\", sys.version.split()[0])\n",
    "\n",
    "DAEMON_PORT = int(os.environ.get(\"SPL_COOKBOOK_DAEMON_PORT\", \"8765\"))\n",
    "NOTEBOOK_NAMESPACE = f\"splime_045_cookbook_{uuid4().hex[:10]}\"\n",
    "\n",
    "DAILY_TOTAL_OBJECT = f\"{NOTEBOOK_NAMESPACE}_daily_total\"\n",
    "ORDER_PIPELINE_OBJECT = f\"{NOTEBOOK_NAMESPACE}_order_pipeline\"\n",
    "IMAGE_PIPELINE_NAME = f\"{NOTEBOOK_NAMESPACE}_image_pipeline\"\n",
    "MATRIX_PIPELINE_OBJECT = f\"{NOTEBOOK_NAMESPACE}_matrix_pipeline\"\n",
    "LARGE_FINITE_OBJECT = f\"{NOTEBOOK_NAMESPACE}_large_finite\"\n",
    "REMOTE_STATUS_PIPELINE = f\"{NOTEBOOK_NAMESPACE}_remote_status\"\n",
    "CLEAN_ENV_OBJECT = f\"{NOTEBOOK_NAMESPACE}_clean_env_probe\"\n",
    "SHARED_LIBRARY_NAME = f\"{NOTEBOOK_NAMESPACE}_library\"\n",
    "\n",
    "CREATED_OBJECT_NAMES: set[str] = set()\n",
    "CREATED_LIBRARY_NAMES: set[str] = set()\n",
    "CREATED_RUN_IDS: set[str] = set()\n",
    "\n",
    "RUN_LOCAL_CLEANUP = False\n",
    "RUN_LIVE_SERVER = False\n",
    "RUN_REMOTE_EXECUTION = False\n",
    "RUN_SHARED_LIBRARY_MUTATIONS = False\n",
    "RUN_CROSS_OWNER = False\n",
    "RUN_ENVIRONMENT_PROBE = False\n",
    "RUN_DOCKER_EXAMPLES = False\n",
    "RUN_EXTERNAL_NETWORK = False\n",
    "RUN_DESTRUCTIVE_ACTIONS = False\n",
    "\n",
    "client = SPLClient(daemon_port=DAEMON_PORT)\n",
    "client.health()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-03",
   "metadata": {},
   "source": [
    "## 2. Offline by default\n",
    "\n",
    "The local client never creates a central-server connection. Part 1 reads the cached\n",
    "connection state without probing a server and skips server-only listings if the\n",
    "daemon was already connected outside this notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-04",
   "metadata": {},
   "outputs": [],
   "source": [
    "connection = client.current_server_connection(probe=False)\n",
    "print(\"central server connected:\", bool(connection.get(\"connected\")))\n",
    "\n",
    "if connection.get(\"connected\"):\n",
    "    print(\"Server-only listings skipped in offline Part 1.\")\n",
    "else:\n",
    "    print(\"machines :\", client.machines())\n",
    "    print(\"libraries:\", client.libraries())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-05",
   "metadata": {},
   "source": [
    "## 3. Publish your first function\n",
    "\n",
    "`publish()` stores the function in the local daemon registry as a versioned object and\n",
    "returns a short receipt. The full daemon document stays available via `receipt.raw`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-06",
   "metadata": {},
   "outputs": [],
   "source": [
    "def daily_total(date: str) -> float:\n",
    "    prices = {\"2026-06-08\": [11.0, 6.5, 24.5]}\n",
    "    return sum(prices.get(date, []))\n",
    "\n",
    "\n",
    "client.register_env(\"default\")\n",
    "receipt = client.publish(daily_total, name=DAILY_TOTAL_OBJECT)\n",
    "CREATED_OBJECT_NAMES.add(DAILY_TOTAL_OBJECT)\n",
    "receipt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-07",
   "metadata": {},
   "outputs": [],
   "source": [
    "client.describe(DAILY_TOTAL_OBJECT)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-08",
   "metadata": {},
   "source": [
    "## 4. Call it by name\n",
    "\n",
    "`call()` runs the object in an isolated worker and returns a `RemoteResult`:\n",
    "\n",
    "- `.output` — the unwrapped return value (use this in the happy path);\n",
    "- `.value` — the daemon's raw result (already plain for functions; a port dict for pipelines);\n",
    "- `.mode` — `'local'` or `'server'`, telling where the run happened."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-09",
   "metadata": {},
   "outputs": [],
   "source": [
    "result = client.call(DAILY_TOTAL_OBJECT, kwargs={\"date\": \"2026-06-08\"})\n",
    "if result.run.get(\"id\"):\n",
    "    CREATED_RUN_IDS.add(str(result.run[\"id\"]))\n",
    "\n",
    "print(result.mode)\n",
    "print(result.output)\n",
    "print(result.value)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-10",
   "metadata": {},
   "source": [
    "## 5. Versions without ceremony\n",
    "\n",
    "Version identity is the content of the function. Republishing identical code is a no-op\n",
    "(you get the same version back). Any real change becomes the **next** version — history\n",
    "is never overwritten."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-11",
   "metadata": {},
   "outputs": [],
   "source": [
    "again = client.publish(daily_total, name=DAILY_TOTAL_OBJECT)\n",
    "print(\"unchanged republish ->\", again.version)\n",
    "\n",
    "\n",
    "def daily_total(date: str) -> float:\n",
    "    prices = {\"2026-06-08\": [11.0, 6.5, 24.5]}\n",
    "    return round(sum(prices.get(date, [])) * 1.1, 2)\n",
    "\n",
    "\n",
    "bumped = client.publish(daily_total, name=DAILY_TOTAL_OBJECT)\n",
    "print(\"changed republish   ->\", bumped.version)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-12",
   "metadata": {},
   "source": [
    "## 6. A pipeline of two functions\n",
    "\n",
    "`lift` wraps a function into a pipeline node, `bind` wires one node's output into\n",
    "another's input, `alias` names an output, `render` fixes the pipeline name.\n",
    "\n",
    "`Deployment(p).run(...)` executes the pipeline **in-process** — a fast dry-run before\n",
    "anything touches the daemon."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-13",
   "metadata": {},
   "outputs": [],
   "source": [
    "def classify_amount(amount: int) -> str:\n",
    "    return \"priority\" if amount == 300 else \"standard\"\n",
    "\n",
    "\n",
    "def build_order(amount: int, bonus: int, status: str, scale: int = 1) -> dict:\n",
    "    total = (amount + bonus) * scale\n",
    "    return {\"amount\": amount, \"bonus\": bonus, \"total\": total, \"status\": status}\n",
    "\n",
    "\n",
    "p = (\n",
    "    lift(build_order)\n",
    "    .bind(status=lift(classify_amount))\n",
    "    .alias(\"result\")\n",
    "    .render(ORDER_PIPELINE_OBJECT)\n",
    ")\n",
    "\n",
    "Deployment(p).run(amount=300, bonus=10, output=\"result\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-14",
   "metadata": {},
   "source": [
    "The same pipeline as a published, versioned object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-15",
   "metadata": {},
   "outputs": [],
   "source": [
    "client.publish(p, name=ORDER_PIPELINE_OBJECT)\n",
    "CREATED_OBJECT_NAMES.add(ORDER_PIPELINE_OBJECT)\n",
    "\n",
    "order = client.call(\n",
    "    ORDER_PIPELINE_OBJECT,\n",
    "    kwargs={\"amount\": 300, \"bonus\": 10},\n",
    "    output=\"result\",\n",
    ")\n",
    "if order.run.get(\"id\"):\n",
    "    CREATED_RUN_IDS.add(str(order.run[\"id\"]))\n",
    "order.output"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-16",
   "metadata": {},
   "source": [
    "## 7. Call a function *inside* a pipeline\n",
    "\n",
    "Inner functions stay callable on their own — two equivalent forms."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-17",
   "metadata": {},
   "outputs": [],
   "source": [
    "inner_result = client.call(\n",
    "    ORDER_PIPELINE_OBJECT,\n",
    "    kwargs={\"amount\": 301},\n",
    "    function=\"classify_amount\",\n",
    ")\n",
    "if inner_result.run.get(\"id\"):\n",
    "    CREATED_RUN_IDS.add(str(inner_result.run[\"id\"]))\n",
    "inner_result.output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-18",
   "metadata": {},
   "outputs": [],
   "source": [
    "shorthand_result = client.call(\n",
    "    f\"{ORDER_PIPELINE_OBJECT}::classify_amount\",\n",
    "    kwargs={\"amount\": 300},\n",
    ")\n",
    "if shorthand_result.run.get(\"id\"):\n",
    "    CREATED_RUN_IDS.add(str(shorthand_result.run[\"id\"]))\n",
    "shorthand_result.output"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-19",
   "metadata": {},
   "source": [
    "## 8. Browse what you have\n",
    "\n",
    "`objects()` prints a compact catalog (name, kind, version, library, inputs).\n",
    "It is still a plain mapping — indexing, iteration and `json.dumps` all work;\n",
    "`.raw` returns the untouched payload."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-20",
   "metadata": {},
   "outputs": [],
   "source": [
    "client.objects()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-21",
   "metadata": {},
   "outputs": [],
   "source": [
    "client.signature(ORDER_PIPELINE_OBJECT)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-22",
   "metadata": {},
   "source": [
    "## 9. Long runs: submit now, collect later\n",
    "\n",
    "`submit()` returns immediately with a run handle. `collect()` waits, checks the status\n",
    "and returns the same `RemoteResult` as `call()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-23",
   "metadata": {},
   "outputs": [],
   "source": [
    "run = client.submit(\n",
    "    ORDER_PIPELINE_OBJECT,\n",
    "    kwargs={\"amount\": 300, \"bonus\": 10},\n",
    "    output=\"result\",\n",
    ")\n",
    "CREATED_RUN_IDS.add(run.id)\n",
    "print(run.status)\n",
    "run.collect().output"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-24",
   "metadata": {},
   "source": [
    "## 10. Values that are not JSON: adapters\n",
    "\n",
    "Nodes exchange JSON-native values by default. For anything else — `bytes`, arrays,\n",
    "models — attach an **adapter** (a save/load pair) in one line. The value crosses process\n",
    "boundaries as an artifact, transparently.\n",
    "\n",
    "Rules of thumb for daemon-safe adapter code: use only what the function itself imports,\n",
    "and keep constants inside the function body."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-25",
   "metadata": {},
   "outputs": [],
   "source": [
    "def render_thumbnail() -> bytes:\n",
    "    return b\"\\x89PNG\\r\\n\\x1a\\n\\x00\\x00\\x00\\rIHDR\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x08\\x06\\x00\\x00\\x00\\x1f\\x15\\xc4\\x89\\x00\\x00\\x00\\x0bIDATx\\xdacd\\xf8\\xcfP\\x0f\\x00\\x03\\x86\\x01\\x80Z4}k\\x00\\x00\\x00\\x00IEND\\xaeB`\\x82\"\n",
    "\n",
    "\n",
    "def describe_image(image: bytes) -> dict:\n",
    "    return {\"n_bytes\": len(image), \"is_png\": image[:8] == b\"\\x89PNG\\r\\n\\x1a\\n\"}\n",
    "\n",
    "\n",
    "def save_bytes(path, obj):\n",
    "    with open(path, \"wb\") as f:\n",
    "        f.write(obj)\n",
    "\n",
    "\n",
    "def load_bytes(path):\n",
    "    with open(path, \"rb\") as f:\n",
    "        return f.read()\n",
    "\n",
    "\n",
    "p_image = (\n",
    "    lift(describe_image)\n",
    "    .bind(image=lift(render_thumbnail))\n",
    "    .alias(\"summary\")\n",
    "    .render(IMAGE_PIPELINE_NAME)\n",
    "    .add_adapter(bytes, \"png\", save=save_bytes, load=load_bytes)\n",
    ")\n",
    "\n",
    "Deployment(p_image).run(output=\"summary\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-26",
   "metadata": {},
   "source": [
    "A realistic type — `numpy.ndarray` — through the daemon. Declaring the adapter's package\n",
    "(`DDistribution`) lets the daemon build the right environment for the worker.\n",
    "\n",
    "*The first call builds a venv with numpy — it can take a couple of minutes; later calls\n",
    "reuse the cached environment.*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-27",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "from spl import DDistribution\n",
    "\n",
    "\n",
    "def make_matrix(seed: int = 7) -> np.ndarray:\n",
    "    return np.random.default_rng(seed).integers(0, 10, size=(2, 3))\n",
    "\n",
    "\n",
    "def summarize_matrix(matrix: np.ndarray) -> dict:\n",
    "    return {\"shape\": list(matrix.shape), \"sum\": int(matrix.sum())}\n",
    "\n",
    "\n",
    "def save_ndarray(path, arr):\n",
    "    with open(path, \"wb\") as f:\n",
    "        np.save(f, arr)\n",
    "\n",
    "\n",
    "def load_ndarray(path):\n",
    "    with open(path, \"rb\") as f:\n",
    "        return np.load(f)\n",
    "\n",
    "\n",
    "p_matrix = (\n",
    "    lift(summarize_matrix)\n",
    "    .bind(matrix=lift(make_matrix))\n",
    "    .alias(\"summary\")\n",
    "    .render(MATRIX_PIPELINE_OBJECT)\n",
    "    .add_adapter(\n",
    "        np.ndarray,\n",
    "        \"npy\",\n",
    "        save=save_ndarray,\n",
    "        load=load_ndarray,\n",
    "        distributions=(DDistribution(\"numpy\", np.__version__),),\n",
    "    )\n",
    ")\n",
    "\n",
    "Deployment(p_matrix).run(output=\"summary\")\n",
    "\n",
    "if RUN_EXTERNAL_NETWORK:\n",
    "    client.publish(p_matrix, name=MATRIX_PIPELINE_OBJECT)\n",
    "    CREATED_OBJECT_NAMES.add(MATRIX_PIPELINE_OBJECT)\n",
    "    matrix_result = client.call(MATRIX_PIPELINE_OBJECT, output=\"summary\")\n",
    "    if matrix_result.run.get(\"id\"):\n",
    "        CREATED_RUN_IDS.add(str(matrix_result.run[\"id\"]))\n",
    "    print(matrix_result.output)\n",
    "else:\n",
    "    print(\"Daemon environment build skipped: RUN_EXTERNAL_NETWORK is False.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-large-finite-text",
   "metadata": {},
   "source": [
    "## 11. Large finite JSON numbers stay exact\n",
    "\n",
    "Python and the daemon preserve finite integers beyond JavaScript's exact-number range.\n",
    "This example asserts the Python value directly; browser code must not add a lossy\n",
    "JavaScript-number equality check. Non-finite floats remain invalid."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-large-finite-code",
   "metadata": {},
   "outputs": [],
   "source": [
    "LARGE_FINITE_INTEGER = 2**53 + 1\n",
    "\n",
    "\n",
    "def large_finite_payload() -> dict:\n",
    "    large_integer = 2**53 + 1\n",
    "    return {\n",
    "        \"positive\": large_integer,\n",
    "        \"negative\": -large_integer,\n",
    "        \"finite_float\": 1e20,\n",
    "    }\n",
    "\n",
    "\n",
    "client.publish(large_finite_payload, name=LARGE_FINITE_OBJECT)\n",
    "CREATED_OBJECT_NAMES.add(LARGE_FINITE_OBJECT)\n",
    "large_finite_result = client.call(LARGE_FINITE_OBJECT)\n",
    "if large_finite_result.run.get(\"id\"):\n",
    "    CREATED_RUN_IDS.add(str(large_finite_result.run[\"id\"]))\n",
    "\n",
    "assert large_finite_result.output[\"positive\"] == LARGE_FINITE_INTEGER\n",
    "assert large_finite_result.output[\"negative\"] == -LARGE_FINITE_INTEGER\n",
    "assert large_finite_result.output[\"finite_float\"] == 1e20\n",
    "large_finite_result.output"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-28",
   "metadata": {},
   "source": [
    "## 12. Housekeeping — explicit opt-in\n",
    "\n",
    "Cleanup is disabled by default. If deliberately enabled, it removes only versions and\n",
    "objects whose namespace was generated by this notebook kernel."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-29",
   "metadata": {},
   "outputs": [],
   "source": [
    "if RUN_LOCAL_CLEANUP and RUN_DESTRUCTIVE_ACTIONS:\n",
    "    if receipt.version != bumped.version:\n",
    "        client.forget_version(DAILY_TOTAL_OBJECT, receipt.version)\n",
    "    for object_name in sorted(CREATED_OBJECT_NAMES):\n",
    "        client.forget(object_name)\n",
    "    print(client.objects())\n",
    "else:\n",
    "    print(\n",
    "        \"Cleanup skipped:\",\n",
    "        len(CREATED_OBJECT_NAMES),\n",
    "        \"namespaced object(s) remain in the local daemon.\",\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-30",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "# Part 2 — Optional connected features\n",
    "\n",
    "Everything above is local. The two existing credential placeholder literals in the\n",
    "next cell remain intentionally unchanged and inert. A connected client is constructed\n",
    "only when both values are positively recognized as configured and both live-network\n",
    "controls are deliberately enabled.\n",
    "\n",
    "Every remote, shared-library, cross-owner, Docker, external-network, and destructive\n",
    "example remains separately disabled by default. Use only disposable, namespaced\n",
    "fixtures when opting in."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-31",
   "metadata": {},
   "outputs": [],
   "source": [
    "from spl import SPLClient\n",
    "\n",
    "USER_TOKEN = \"...\"\n",
    "MACHINE_TOKEN = \"...\"\n",
    "\n",
    "_CREDENTIAL_PLACEHOLDERS = {\n",
    "    \"\",\n",
    "    \"...\",\n",
    "    \"replace-me\",\n",
    "    \"replace_me\",\n",
    "    \"your-token\",\n",
    "    \"your_token\",\n",
    "}\n",
    "\n",
    "\n",
    "def _is_configured_credential(value: object) -> bool:\n",
    "    if not isinstance(value, str):\n",
    "        return False\n",
    "    normalized = value.strip()\n",
    "    if normalized.lower() in _CREDENTIAL_PLACEHOLDERS:\n",
    "        return False\n",
    "    if normalized.startswith(\"<\") and normalized.endswith(\">\"):\n",
    "        return False\n",
    "    return len(normalized) >= 16 and not any(character.isspace() for character in normalized)\n",
    "\n",
    "\n",
    "credentials_configured = all(\n",
    "    _is_configured_credential(value)\n",
    "    for value in (USER_TOKEN, MACHINE_TOKEN)\n",
    ")\n",
    "\n",
    "if RUN_LIVE_SERVER and RUN_EXTERNAL_NETWORK and credentials_configured:\n",
    "    client = SPLClient(\n",
    "        user_token=USER_TOKEN,\n",
    "        machine_token=MACHINE_TOKEN,\n",
    "    )\n",
    "    connection = client.current_server_connection()\n",
    "    print(\"Central server connected:\", bool(connection.get(\"connected\")))\n",
    "else:\n",
    "    connection = client.current_server_connection(probe=False)\n",
    "    print(\"Connected section skipped: controls are off or credentials are placeholders.\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-32",
   "metadata": {},
   "source": [
    "## 13. Run it where the data lives\n",
    "\n",
    "Remote execution is separately opt-in and never guesses a machine identifier."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-33",
   "metadata": {},
   "outputs": [],
   "source": [
    "TARGET_MACHINE = None\n",
    "\n",
    "if RUN_REMOTE_EXECUTION and RUN_LIVE_SERVER and connection.get(\"connected\"):\n",
    "    TARGET_MACHINE = client.machines().get(\"current_machine_id\")\n",
    "    if TARGET_MACHINE:\n",
    "        remote = client.call(\n",
    "            ORDER_PIPELINE_OBJECT,\n",
    "            kwargs={\"amount\": 300, \"bonus\": 10},\n",
    "            output=\"result\",\n",
    "            target_machine=TARGET_MACHINE,\n",
    "        )\n",
    "        if remote.run.get(\"id\"):\n",
    "            CREATED_RUN_IDS.add(str(remote.run[\"id\"]))\n",
    "        print(remote.mode)\n",
    "        print(remote.output)\n",
    "    else:\n",
    "        print(\"Remote execution skipped: no namespaced disposable machine fixture.\")\n",
    "else:\n",
    "    print(\"Remote execution skipped: RUN_REMOTE_EXECUTION is False.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-34",
   "metadata": {},
   "source": [
    "## 14. NodeRemote — a published function as a node in a new pipeline\n",
    "\n",
    "`NodeRemote` references a published object so it can be wired into a local pipeline.\n",
    "The example uses only this notebook's object and requires the same explicit remote opt-in."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-35",
   "metadata": {},
   "outputs": [],
   "source": [
    "if RUN_REMOTE_EXECUTION and RUN_LIVE_SERVER and TARGET_MACHINE:\n",
    "    remote_classify = NodeRemote.locate(\n",
    "        pipeline=ORDER_PIPELINE_OBJECT,\n",
    "        function=\"classify_amount\",\n",
    "        target_machine=TARGET_MACHINE,\n",
    "    )\n",
    "    p_mixed = (\n",
    "        lift(remote_classify)\n",
    "        .bind(amount=301)\n",
    "        .alias(\"status\")\n",
    "        .render(REMOTE_STATUS_PIPELINE)\n",
    "    )\n",
    "    print(Deployment(client, p_mixed).run(output=\"status\"))\n",
    "else:\n",
    "    print(\"NodeRemote skipped: remote execution is disabled or no fixture is available.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-36",
   "metadata": {},
   "source": [
    "## 15. Teams: libraries, grants, references\n",
    "\n",
    "Library mutations require a live connection, an explicit mutation opt-in, and a\n",
    "deliberately configured disposable grantee. The library and object names stay inside\n",
    "the notebook namespace."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-37",
   "metadata": {},
   "outputs": [],
   "source": [
    "SHARED_LIBRARY_GRANTEE = \"@grantee-placeholder\"\n",
    "grantee_configured = not SHARED_LIBRARY_GRANTEE.endswith(\"-placeholder\")\n",
    "\n",
    "if (\n",
    "    RUN_SHARED_LIBRARY_MUTATIONS\n",
    "    and RUN_LIVE_SERVER\n",
    "    and connection.get(\"connected\")\n",
    "    and grantee_configured\n",
    "):\n",
    "    client.library.create(\n",
    "        SHARED_LIBRARY_NAME,\n",
    "        display_name=SHARED_LIBRARY_NAME,\n",
    "        visibility=\"private\",\n",
    "    )\n",
    "    CREATED_LIBRARY_NAMES.add(SHARED_LIBRARY_NAME)\n",
    "    client.publish(\n",
    "        p,\n",
    "        name=ORDER_PIPELINE_OBJECT,\n",
    "        library=SHARED_LIBRARY_NAME,\n",
    "    )\n",
    "    client.library.grant(\n",
    "        SHARED_LIBRARY_NAME,\n",
    "        SHARED_LIBRARY_GRANTEE,\n",
    "        scopes=[\"metadata:read\", \"objects:read\", \"execute\"],\n",
    "    )\n",
    "    print(client.libraries())\n",
    "else:\n",
    "    print(\"Shared-library mutation skipped: safe controls or fixture are not enabled.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-38",
   "metadata": {},
   "source": [
    "## 16. Other people's objects\n",
    "\n",
    "Cross-owner reads require a deliberately configured disposable owner handle and remain\n",
    "disabled independently of every other connected example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cell-39",
   "metadata": {},
   "outputs": [],
   "source": [
    "OWNER_HANDLE = \"@owner-placeholder\"\n",
    "owner_configured = not OWNER_HANDLE.endswith(\"-placeholder\")\n",
    "\n",
    "if RUN_CROSS_OWNER and RUN_LIVE_SERVER and connection.get(\"connected\") and owner_configured:\n",
    "    print(client.objects(scope=\"server\", owner=OWNER_HANDLE))\n",
    "else:\n",
    "    print(\"Cross-owner example skipped: RUN_CROSS_OWNER is False or no fixture is configured.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d8756e58f36",
   "metadata": {},
   "source": [
    "## 17. Your node's environment stays clean\n",
    "\n",
    "In 0.4.5, regular functions use the dependency-free runner when supported. The local\n",
    "probe is namespaced and separately opt-in; inspecting environment builds and any\n",
    "Docker-backed work has its own false-by-default control."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ed9a0fd221b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "def clean_env_probe(x: int = 2) -> int:\n",
    "    return x * 21\n",
    "\n",
    "\n",
    "if RUN_ENVIRONMENT_PROBE:\n",
    "    client.publish(clean_env_probe, name=CLEAN_ENV_OBJECT)\n",
    "    CREATED_OBJECT_NAMES.add(CLEAN_ENV_OBJECT)\n",
    "    clean_result = client.call(CLEAN_ENV_OBJECT, kwargs={\"x\": 2})\n",
    "    if clean_result.run.get(\"id\"):\n",
    "        CREATED_RUN_IDS.add(str(clean_result.run[\"id\"]))\n",
    "    print(clean_result.output)\n",
    "\n",
    "    clean_run = next(\n",
    "        item for item in client.runs()\n",
    "        if item.get(\"object\") == CLEAN_ENV_OBJECT\n",
    "    )\n",
    "    print(clean_run.get(\"worker_runtime\"))\n",
    "else:\n",
    "    print(\"Environment probe skipped: RUN_ENVIRONMENT_PROBE is False.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "656e9a7c18c9",
   "metadata": {},
   "outputs": [],
   "source": [
    "if RUN_ENVIRONMENT_PROBE and RUN_DOCKER_EXAMPLES:\n",
    "    for build in client.environment_builds()[:3]:\n",
    "        print(\n",
    "            str(build.get(\"spec_hash\", \"\"))[:12],\n",
    "            \"| builder:\",\n",
    "            build.get(\"builder\"),\n",
    "            \"|\",\n",
    "            build.get(\"status\"),\n",
    "        )\n",
    "else:\n",
    "    print(\"Build/Docker inspection skipped: both controls are False by default.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cell-40",
   "metadata": {},
   "source": [
    "## Checklist\n",
    "\n",
    "Offline (Part 1):\n",
    "\n",
    "- Marker: `splime==0.4.5`, updated 2026-07-27.\n",
    "- One generated namespace covers objects, pipelines, libraries, and tracked daemon run IDs.\n",
    "- `SPLClient` uses only the local daemon; cached connection state is read with `probe=False`.\n",
    "- `publish`, `call`, `describe`, `objects`, `signature`, `submit`, and `collect` use current public APIs.\n",
    "- `lift` → `bind` → `alias` → `render`; `Deployment(...).run(...)` is the in-process dry-run.\n",
    "- Adapters carry non-JSON values; distribution-backed daemon builds stay behind the external-network flag.\n",
    "- Finite integers beyond JavaScript's exact range round-trip exactly through Python and the daemon.\n",
    "- Cleanup is opt-in and targets only names recorded by this notebook.\n",
    "\n",
    "Connected (Part 2):\n",
    "\n",
    "- Placeholder credentials never construct a connected client.\n",
    "- Server, remote, shared-library, cross-owner, environment, Docker, external-network, and destructive controls are `False`.\n",
    "- `NodeRemote`, `client.library.*`, and owner-scoped reads use only explicit disposable fixtures when enabled."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
