DEV Community

Riley Li
Riley Li

Posted on

Inventory What You Cannot Pin Before You Choose Free Agent Compute

Free shared compute is a good lab bench when overnight drift is cheap to absorb. A pinned box is the better home when next Tuesday's agent run must still match today's trace. I start from that split each time, rather than from a price tag or an idle GPU. Can you name every moving part this agent will still need next week?

A host chosen from an empty queue often fails when a credential, a model identity, or a disk layout moves. I do not treat "is free cheaper than a box I run" as the real question sitting in front of the loop. I ask which parts of the agent loop refuse to be pinned at all, then I choose a host. That question also decides when a slick Friday demo is still only a lab accident.

Overnight drift is the decision, not the invoice

Agent loops are not single HTTP handlers sitting behind a frozen OpenAPI contract you can file away. They stitch a model, a tool surface, a secret boundary, a timeout owner, and a trace sink together in one session. If any of those pieces floats overnight, Friday's success story is a mood rather than evidence you can hand a reviewer. Have you tried replaying last week's failing tool call without guessing the model snapshot that answered?

I use a drift inventory because a cost model lies when the environment itself is the moving target under the agent. Free shared capacity can still be the right first room while the tool surface is changing twice a day. It becomes the wrong room when a reviewer needs a reconstructable failure and was never in the original chat. This whole article is a proposed workflow, not a production study with logos or latency medals.

Build the inventory before you request a host

I write seven fields down before I decide where the loop is allowed to run at all. Each field is pinned, exportable, or floating, and I refuse to mix those three words casually in a standup. Floating is allowed during exploration, and I say that out loud so nobody promotes a mood into a release. Floating is not allowed after a run is treated as a decision record that other people must trust.

The seven fields

  1. Model identity. I ask whether I can name a pin, or whether I am taking whatever answers today.
  2. Runtime and dependencies. I ask whether the same tool runner and lockfile can be reinstalled without folklore.
  3. Tool origin. I ask whether tool calls execute on a network I can describe to another engineer.
  4. Secret boundary. I ask whether API keys ever enter a host that I cannot image or wipe.
  5. Filesystem. I ask whether working trees, caches, and scratch files are objects I can snapshot later.
  6. Timeout and retry owner. I ask who declares a hung tool dead, me or a shared scheduler.
  7. Trace export. I ask whether I leave with JSONL traces and prompts, or only a dashboard screenshot.

If four or more fields are floating, I stay on a free lab bench and I refuse promotion. If four or more fields must be pinned, I want a box I operate or paid isolation I can describe. Mixed scores mean a two-stage path: explore while floating is cheap, then promote only the pinned slice. Does that feel heavier than "just run it somewhere and see"? Agents hide real cost inside the parts you cannot even name.

Run a scorecard instead of arguing in chat

I keep the inventory in YAML so a reviewer can diff it like any other contract checked into git. The helper script below is a proposed checklist, and it does not call a vendor API or invent a benchmark. I also do not treat the integer score as a latency number or as a security control that replaces threat modeling.

# agent_env.yaml — proposed inventory, fill honestly
agent: invoice-triage-loop
stage: explore  # explore | promote
fields:
  model_identity: floating    # pinned | exportable | floating
  runtime_lockfile: pinned
  tool_origin: floating
  secret_boundary: floating
  filesystem: exportable
  timeout_owner: floating
  trace_export: exportable
notes:
  secrets: "provider tokens currently injected by the shared runner"
  traces: "we can download JSONL, not the raw sandbox disk"
Enter fullscreen mode Exit fullscreen mode
#!/usr/bin/env python3
"""drift_score.py — proposed scorecard, not measured production data."""

from __future__ import annotations

import sys
from pathlib import Path

try:
    import yaml
except ImportError:
    print("pip install pyyaml", file=sys.stderr)
    raise

WEIGHTS = {
    "pinned": 0,
    "exportable": 1,
    "floating": 2,
}

PROMOTE_BLOCKERS = {
    "secret_boundary": "floating",
    "model_identity": "floating",
    "trace_export": "floating",
}


def load_inventory(path: Path) -> dict:
    data = yaml.safe_load(path.read_text())
    if "fields" not in data:
        raise ValueError("inventory needs a fields map")
    return data


def score(fields: dict[str, str]) -> tuple[int, list[str]]:
    total = 0
    blockers = []
    for name, state in fields.items():
        if state not in WEIGHTS:
            raise ValueError(f"{name}: unknown state {state!r}")
        total += WEIGHTS[state]
        if PROMOTE_BLOCKERS.get(name) == state:
            blockers.append(name)
    return total, blockers


def recommend(stage: str, total: int, blockers: list[str]) -> str:
    if stage == "promote" and blockers:
        joined = ", ".join(blockers)
        return f"stay off promotion: pin {joined} before leaving the lab bench"
    if total <= 4:
        return "free shared compute is a fit while drift stays cheap"
    if total <= 8:
        return "split the loop: explore free, pin the promote slice elsewhere"
    return "run the loop on a box you can pin and export"


def main() -> None:
    path = Path(sys.argv[1] if len(sys.argv) > 1 else "agent_env.yaml")
    data = load_inventory(path)
    total, blockers = score(data["fields"])
    print(f"agent={data.get('agent')}")
    print(f"stage={data.get('stage')}")
    print(f"drift_score={total} (0=pinned, higher=more overnight risk)")
    print(f"promote_blockers={blockers or 'none'}")
    print(recommend(data.get("stage", "explore"), total, blockers))


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode
python3 -m pip install pyyaml
python3 drift_score.py agent_env.yaml
Enter fullscreen mode Exit fullscreen mode

I treat a drift score at or under four as permission to remain on free shared capacity for now. I treat any floating secret_boundary as a hard promotion block, even when the rest of the card looks tidy. Would you ship a loop whose keys live on a host you cannot image after a bad run?

Where a free lab bench still wins the comparison

Exploration wants cheap retries, throwaway sandboxes, and models you can try without opening a purchase order first. That is the honest job of free model access paired with a free server option during the explore stage. I use that room to fill the YAML honestly, not to pretend the YAML is already pinned for a reviewer who missed the chat.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

MonkeyCode is an open-source project with free model access and a free server option that maps onto this explore stage. I mention it because the inventory needs a lab bench, not because a free host pins production secrets for you. If most fields are still floating and the stage is still explore, that lab is a rational place to generate traces you can actually export. I do not claim model names, token quotas, hardware SKUs, or how long any free tier remains available.

Can you live with that uncertainty while the loop is still changing twice a day on purpose? If you can, free compute is doing exactly the job I want from an early lab. If you cannot, you already know you need a pin, and shopping for spare capacity will not create one.

A labeled walkthrough I have not billed as a case study

This walkthrough is a proposed example with fake ticket names, and I am not attaching measured production timings to it. Imagine an invoice-triage agent that calls a PDF parser, a vendor HTTP API, and a local sqlite cache. During week one I let model identity float, I inject the vendor token through the shared runner, and I download JSONL after each session. The scorecard comes back high, blockers include secret_boundary and model_identity, and the only honest recommendation is to remain in explore.

During week two I lock a requirements file, I move the vendor token into a store I operate, and I pin tool origin to a container I build. Floating fields drop on purpose, and only then do I copy the same fixtures onto isolated compute I can describe. Notice what did not need to change between those weeks: the prompts, the expected tool names, and the JSONL schema the lab already exported. The host changed because the inventory changed, not because a coupon expired or a queue looked lonely.

# proposed local pin after the lab stage — replace with your runtime
cp agent_env.yaml pinned_env.yaml
# edit model_identity, secret_boundary, and tool_origin to pinned
python3 drift_score.py pinned_env.yaml
Enter fullscreen mode Exit fullscreen mode

If the promote command still prints blockers, I do not "just try prod" to soothe a demo calendar that wants a green box. I fix the field that refused to be pinned, then I run the scorecard again before anyone changes the stage. Why would a green demo override a floating secret boundary that I already wrote down in YAML?

Explicit tradeoffs I put on the same card

Free shared models and a free server

Free shared capacity fits exploration, prompt drafts, throwaway tools, and traces that leave with me in JSONL. The tradeoff is that model identity and noisy-neighbor timing may float overnight without anyone calling a meeting. I refuse this option when someone must reconstruct a failure for a reviewer who never sat in the original session.

Self-hosted or paid isolated compute

A box I pin fits locked runtimes, secret stores I own, and tool networks I can draw on a whiteboard. The tradeoff is that I now own disk, upgrades, and the pager for hung tool calls after midnight. I refuse this option for loops that still change their tool surface twice a day and cannot sit still long enough to pin.

A split path with portable fixtures

A split path fits most agents that start messy and later need a reconstructable record instead of a chat screenshot. The tradeoff is that fixtures must stay portable, or the split quietly becomes two incompatible products with folklore in the middle. I refuse a split when the lab cannot export traces, because then the isolated box is pinning theater rather than evidence.

I want fixtures to stay boring: JSONL prompts, expected tool names, and timeout budgets that survive a host change without a war story. If the lab cannot export that bundle, free compute is teaching a demo habit I do not trust on Monday. If the isolated box cannot import that bundle, I bought a pin I cannot test when the next incident shows up.

Who should not use this approach

Do not use this inventory as a substitute for threat modeling, license review, or a real secret scanner in CI. Do not use the numeric score as a performance benchmark, because the weights are conversation aids I chose in this draft. Do not run untrusted agent code on a free shared host and then point at YAML as if it were a control plane.

Skip the method if your so-called agent is a stateless function with no tools, no secrets, and no session to reconstruct. A normal unit test is enough in that boring case, and the inventory would only add ceremony around a function. Skip it if you cannot tell the truth inside the YAML, because a lying inventory is worse than an empty file.

I also skip free shared compute when the loop must touch production data, even when the meeting is labeled as a harmless demo for leadership. Drift is not the only risk on the table when an agent can call tools. Access is a different risk, and this scorecard does not pretend to cover production data paths.

Monday morning steps

  1. I write agent_env.yaml before I request a host, including the stage field set to explore.
  2. I run drift_score.py and I read the promote blockers out loud before anyone books a demo.
  3. I generate traces on a free lab bench only while that stage value remains explore.
  4. I export JSONL, lockfiles, and the YAML itself so the next host does not inherit folklore.
  5. I pin secrets, tool origin, and model identity on a box I control before I change the stage.
  6. I re-run the scorecard and I refuse promotion whenever any blocker is still marked floating.

Free versus self-hosted is a function of what you cannot pin, not a personality test about cloud loyalty. I would rather keep an agent in the lab for another week than promote a loop whose environment I cannot name. If you cannot name what will change overnight in your loop, you are not ready to pick a host.

Top comments (0)